CRUD operations
Here is a simple example of creating RESTful API with all the CRUD operations on posts
We want this API to be JSON only. So first let's set default Content-Type
for our router (Read more about it at Routing) to application/json
:
const router = new Router({
type: 'application/json'
})
Setting default Content-Type
will make all the successful responses rendered as JSON automatically. Next we're going to make all the errors render as JSON:
const errorHandler = (_: Context, error: HTTPError) => {
return new HTTPResponse({
status: error.code,
type: 'application/json',
body: { ok: false, message: error.message }
})
}
const app = new Application()
app.handleErrors(errorHandler)
Now we just need to declare all the route handlers, which can be seen in the full code snippet below:
// crud.ts
import {
Application,
HTTPResponse,
HTTPError,
HTTPStatus,
Context,
Router,
BadRequestError,
NotFoundError
} from 'jsr:@sequoia/sequoia'
// Custom error handler to display errors as JSON
const errorHandler = (_: Context, error: HTTPError) => {
return new HTTPResponse({
status: error.code,
type: 'application/json',
body: { ok: false, message: error.message }
})
}
const app = new Application()
app.handleErrors(errorHandler)
const router = new Router({
type: 'application/json'
})
interface Post {
id: number
text: string
}
let posts: Post[] = []
const isValid = (id: number) => !isNaN(id) && Number.isInteger(id) && id > 0
router.GET('/posts', () => new HTTPResponse({ body: posts }))
router.GET('/posts/:id', (context: Context<{ id?: string }>) => {
const { id } = context.request.params
if (!id || !isValid(+id)) {
throw new BadRequestError('Please specify a correct post id')
}
const post = posts.find((post) => post.id === +id)
if (!post) {
throw new NotFoundError('Post with specified id does not exist')
}
return new HTTPResponse({ body: post })
})
router.POST('/posts', async (context) => {
const { id, text } = (await context.request.json()) as Partial<Post>
if (!id || !text) {
throw new BadRequestError('Fields "id" and "text" are required')
}
if (!isValid(id)) {
throw new BadRequestError('Field "id" must be an integer')
}
if (posts.find((post) => post.id === +id)) {
throw new BadRequestError('A post with specified id already exists')
}
posts.push({ id, text })
return new HTTPResponse({ status: HTTPStatus.CREATED, body: { ok: true } })
})
router.PUT('/posts/:id', async (context: Context<{ id?: string }>) => {
const { id } = context.request.params
const { text } = (await context.request.json()) as { text?: string }
if (!id || !isValid(+id) || !text) {
throw new BadRequestError('Please specify a correct post id and text')
}
const index = posts.findIndex((post) => post.id === +id)
if (index === -1) {
throw new NotFoundError('Post with specified id does not exist')
}
posts[index] = {
text,
id: posts[index].id
}
return new HTTPResponse({ body: { ok: true } })
})
router.DELETE('/posts/:id', (context: Context<{ id?: string }>) => {
const { id } = context.request.params
if (!id || !isValid(+id)) {
throw new BadRequestError('Please specify a correct post id')
}
const post = posts.find((post) => post.id === +id)
if (!post) {
throw new NotFoundError('Post with specified id does not exist')
}
posts = posts.filter((post) => post.id === +id)
return new HTTPResponse({ body: { ok: true } })
})
app.useRouter(router)
await app.listen({ port: 8000 })
Run it with deno run --allow-net crud.ts
Or visit our example hosted at Deno Deploy