bookmonkey-api
Version:
A simple backend for the BookMonkey example application.
632 lines (591 loc) • 19.8 kB
HTML
<html>
<head>
<meta charset="UTF-8" />
<title>Bookmonkey API</title>
<meta name="robots" content="noindex, follow" />
<link rel="stylesheet" href="bootstrap.min.css" />
<link rel="stylesheet" href="highlight.androidstudio.css" />
<script src="highlight.pack.js"></script>
<script>
hljs.initHighlightingOnLoad();
</script>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-toggleable navbar-inverse bg-primary">
<div class="container">
<a class="navbar-brand" href="#">Bookmonkey API</a>
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="#list">List</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#detail">Detail</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#create">Create</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#update">Update</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#delete">Delete</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#operations">Further Operations</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#authentication">Authentication</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#users">User Relation</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#guarded_routes">Guarded Routes</a>
</li>
</ul>
</div>
</nav>
<!-- Content -->
<main class="container mt-4">
<!-- Book List -->
<h2 id="list">Book List</h2>
<p>GET <a href="/books" target="_blank">/books</a></p>
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
[
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png"
},
…
]
</code>
</pre>
<!-- Book Detail -->
<h2 id="detail">Book Detail</h2>
<p>
GET <a href="/books/9781787125421" target="_blank">/books/:isbn</a>
</p>
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png"
}
</code>
</pre>
<!-- Book Create -->
<h2 id="create">Book Create</h2>
<p>POST <a href="/books" target="_blank">/books</a></p>
<p>Include a <code>Content-Type: application/json</code> header to use the JSON in the request body. Otherwise it will return a 2XX status code, but without changes being made to the data.</p>
<p>Title and ISBN are required fields.</p>
<div class="row">
<div class="col-6">
<strong>Payload:</strong>
<pre class="mb-4">
<code class="json">
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png"
}
</code>
</pre>
</div>
<div class="col-6">
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png"
}
</code>
</pre>
</div>
</div>
<strong>Error Response</strong>
<p>400 Status Code</p>
<pre class="mb-4">
<code class="json">
{
"errors": {
"isbn": {
"msg": "Es muss eine ISBN angegeben werden.",
"param": "isbn",
"location": "body"
},
"title": {
"msg": "Es muss ein Titel angegeben werden.",
"param": "title",
"location": "body"
}
}
}
</code>
</pre>
<!-- Book Update -->
<h2 id="update">Book Update</h2>
<p>
PUT <a href="/books/9781787125421" target="_blank">/books/:isbn</a>
</p>
<p>Include a <code>Content-Type: application/json</code> header to use the JSON in the request body. Otherwise it will return a 2XX status code, but without changes being made to the data.</p>
<p>Title and ISBN are required fields.</p>
<div class="row">
<div class="col-6">
<strong>Payload:</strong>
<pre class="mb-4">
<code class="json">
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png"
}
</code>
</pre>
</div>
<div class="col-6">
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png"
}
</code>
</pre>
</div>
</div>
<strong>Error Response</strong>
<p>400 Status Code</p>
<pre class="mb-4">
<code class="json">
{
"errors": {
"isbn": {
"msg": "Es muss eine ISBN angegeben werden.",
"param": "isbn",
"location": "body"
},
"title": {
"msg": "Es muss ein Titel angegeben werden.",
"param": "title",
"location": "body"
}
}
}
</code>
</pre>
<!-- Book Delete -->
<h2 id="delete">Book Delete</h2>
<p>
DELETE
<a href="/books/9781787125421" target="_blank">/books/:isbn</a>
</p>
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
Ok
</code>
</pre>
<h2 id="operations">Operations</h2>
<!-- Pagination -->
<h2 id="pagination">Pagination</h2>
<p>GET <a href="/books?_page=2&_limit=10" target="_blank">/books?_page=2&_limit=10</a></p>
<p>Use <code>_page</code> and optionally <code>_limit</code> to paginate returned data. 10 items are returned by default</p>
<p>In the <code>Link</code> header you'll get <code>first</code>, <code>prev</code>, <code>next</code> and <code>last</code> links.</p>
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
[
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png"
},
…
]
</code>
</pre>
<!-- Sort -->
<h2 id="sort">Sort</h2>
<p>GET <a href="/books?_sort=title&_order=desc" target="_blank">/books?_sort=title&_order=desc</a></p>
<p>Add <code>_sort</code> and <sort>_order</sort> (ascending order by default)</p>
<p>Use a comma-separated list for multiple fields.</p>
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
[
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png"
},
…
]
</code>
</pre>
<!-- Slice -->
<h2 id="slice">Slice</h2>
<p>GET <a href="/books?_start=10&_end=20" target="_blank">/books?_start=10&_end=20</a></p>
<p>Add <code>_start</code> and <code>_end</code> or <code>_limit</code> (an <code>X-Total-Count</code> header is included in the response)</p>
<p>Works exactly as <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice" target="_blank">Array.slice</a> (i.e. <code>_start</code> is inclusive and <code>_end</code> exclusive)</p>
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
[
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png"
},
…
]
</code>
</pre>
<!-- Operators -->
<h2 id="operators">Operators</h2>
<p>GET <a href="/books?numPages_gte=100&numPages_lte=500" target="_blank">/books?numPages_gte=100&numPages_lte=500</a></p>
<p>Add <code>_gte</code> or <code>_lte</code> for getting a range</p>
<p>Add <code>_ne</code> to exclude a value</p>
<p>Add <code>_like</code> to filter (RegExp supported)</p>
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
[
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png"
},
…
]
</code>
</pre>
<!-- Full-text search -->
<h2 id="search">Full-text search</h2>
<p>GET <a href="/books?q=vue" target="_blank">/books?q=vue</a></p>
<p>Add <code>q</code> for a full-text search</p>
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
[
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png"
},
…
]
</code>
</pre>
<h2 id="authentication">Authentication</h2>
<!-- Register -->
<h2 id="register">Register</h2>
<p>POST <a href="/register" target="_blank">/register</a></p>
<p>The response contains the JWT access token (expiration time of 1 hour). The access token has the following claims:
<ul>
<li><code>sub:</code> the user id.</li>
<li><code>email:</code> the user email.</li>
</ul>
</p>
<div class="row">
<div class="col-6">
<strong>Payload:</strong>
<pre class="mb-4">
<code class="json">
{
email: "youremail@demo.com",
password: "bestPassw0rd",
}
</code>
</pre>
</div>
<div class="col-6">
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
{
accessToken: "xxx.xxx.xxx",
user: {
email: "email@domain.com",
password: "HASH",
id: 1
}
}
</code>
</pre>
</div>
</div>
<strong>Other propreties</strong>
<p>Any other property can be added to the request body without being validated</p>
<pre class="mb-4">
<code class="json">
{
email: "youremail@demo.com",
password: "bestPassw0rd",
firstname: "Max",
lastname: "Mustermann",
age: 101
}
</code>
</pre>
<!-- Login -->
<h2 id="login">Login</h2>
<p>POST <a href="/login" target="_blank">/login</a></p>
<p>The response contains the JWT access token (expiration time of 1 hour). The access token has the following claims:
<ul>
<li><code>sub:</code> the user id.</li>
<li><code>email:</code> the user email.</li>
</ul>
</p>
<div class="row">
<div class="col-6">
<strong>Payload:</strong>
<pre class="mb-4">
<code class="json">
{
email: "youremail@demo.com",
password: "bestPassw0rd",
}
</code>
</pre>
</div>
<div class="col-6">
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
{
accessToken: "xxx.xxx.xxx",
user: {
email: "email@domain.com",
password: "HASH",
id: 1
}
}
</code>
</pre>
</div>
</div>
<!-- User Update -->
<h2 id="user_update">User Update</h2>
<p>Any update to an existing user (via PATCH or PUT methods) will go through the same process for email and password.</p>
<p>PUT <a href="/users/1" target="_blank">/users/:id</a> <br/>
PATCH <a href="/users/1" target="_blank">/users/:id</a></p>
<h2 id="user_default_login" class="mt-5">Admin User - Login Data</h2>
<p>There is already a demo user inserted into the database. The credentials to login as this admin user are as follows:
<ul>
<li><code>admin@bookmonkey.api</code></li>
<li><code>password1!</code></li>
</ul>
</p>
<!-- Users -->
<h2 id="users" class="mt-5">User Relation</h2>
<p>A book can be linked to a specific user by sending the <code>userId</code> parameter when adding a book via <code>POST</code>.</p>
<div class="row">
<div class="col-6">
<strong>Payload:</strong>
<pre class="mb-4">
<code class="json">
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png",
userId: "1"
}
</code>
</pre>
</div>
<div class="col-6">
<strong>Response:</strong>
<pre class="mb-4">
<code class="json">
{
title: "…",
subtitle: "…",
isbn: "…",
abstract: "…",
numPages: 123,
author: "…",
publisher: "…",
price: "$..",
cover: "http://….png",
userId: "1"
}
</code>
</pre>
</div>
</div>
<h3>Relational Request</h3>
<p>You can retrieve a book list for a specific user with the nested book route.</p>
<p>PUT <a href="/users/1/books" target="_blank">/users/:id/books</a></p>
<h2 id="guarded_routes" class="mt-5">Guarded Routes</h2>
<div class="row">
<div class="col-12">
<p>Guarded routes exist at the root and can restrict access to any resource you put after them:</p>
<table class="w-100">
<thead>
<tr>
<th width="30%">Route</th>
<th width="70%">Resource permissions</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/664/*</code></td>
<td>User must be logged to <i>write</i> the resource. <br> Everyone can <i>read</i> the resource.</td>
</tr>
<tr>
<td><code>/660/*</code></td>
<td>User must be logged to <i>write</i> and <i>read</i> the resource.</td>
</tr>
<tr>
<td><code>/644/*</code></td>
<td>User must own the resource to <i>write</i> the resource. <br> Everyone can <i>read</i> the resource.</td>
</tr>
<tr>
<td><code>/640/*</code></td>
<td>User must own the resource to <i>write</i> the resource. <br> User must be logged to <i>read</i> the resource.</td>
</tr>
<tr>
<td><code>/600/*</code></td>
<td>User must own the resource to <i>write</i> or <i>read</i> the resource.</td>
</tr>
<tr>
<td><code>/444/*</code></td>
<td>No one can <i>write</i> the resource. <br> Everyone can <i>read</i> the resource.</td>
</tr>
<tr>
<td><code>/440/*</code></td>
<td>No one can <i>write</i> the resource. <br> User must be logged to <i>read</i> the resource.</td>
</tr>
<tr>
<td><code>/400/*</code></td>
<td>No one can <i>write</i> the resource. <br> User must own the resource to <i>read</i> the resource.</td>
</tr>
</tbody>
</table>
<h3 class="mt-2">Examples:</h3>
<p>Public user (not logged-in) does the following requests:</p>
<table class="w-100">
<thead>
<tr>
<th width="30%">Request</th>
<th width="70%">Response</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>GET /664/books</code></td>
<td><code>200 OK</code></td>
</tr>
<tr>
<td><code>POST /664/books</code><br><code>{isbn: '123123123'}</code></td>
<td><code>401 UNAUTHORIZED</code></td>
</tr>
</tbody>
</table>
<p class="mt-3">Logged-in user with <code>id: 1</code> does the following requests:</p>
<table class="w-100">
<thead>
<tr>
<th width="30%">Request</th>
<th width="70%">Response</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>GET /600/users/1</code><br><code>Authorization: Bearer xxx.xxx.xxx</code></td>
<td><code>200 OK</code></td>
</tr>
<tr>
<td><code>GET /600/users/42</code><br><code>Authorization: Bearer xxx.xxx.xxx</code></td>
<td><code>403 FORBIDDEN</code></td>
</tr>
</tbody>
</table>
</div>
</div>
</main>
</body>
</html>