Table of contents
In part 1, we have already set up our project. In this part, we will familiarize ourselves with the basic concepts of Fastify, which are essential for creating REST APIs.
If you wish to follow along, you can clone the Part I branch. For the complete code, you can refer to the Part 2 branch.
In this section, we will create some simple endpoints for saving and fetching movies, and along the way, we will explore some important Fastify concepts.
Let's create the following three endpoints:
GET /movies
POST /movies
GET /movies/:movieGenre
GET Request
Inside the src
folder, create a file app.ts
and add the following code:
import { type FastifyInstance, type FastifyPluginOptions } from 'fastify'
interface FavMovies {
title: string
description: string
genre: string
}
/** Our simple database for movies **/
const favMovies: FavMovies[] = [];
export default async function (
fastify: FastifyInstance,
opts: FastifyPluginOptions,
): Promise<void> {
fastify.route({
url: '/movies',
method: 'GET',
handler: function myHandler(request, reply) {
reply.send({
message: 'Movies listed successfully',
success: true,
data: favMovies
})
},
})
}
The code above demonstrates the simplest way to create an API endpoint using Fastify. The route
method accepts a JSON object as a parameter, where we define our request handler and the endpoint coordinates.
The handler
function should be familiar if you've worked with frameworks like Express. The business logic can be included in the handler. The HTTP request is available in the request
argument, and the response to the client can be handled using the reply
object.
Before this endpoint can work, we need to start our application. To do that, add the following code to index.ts
:
import Fastify from 'fastify'
import App from './app'
async function start(): Promise<void> {
const fastify = Fastify({
logger: true,
})
await fastify.register(App)
await fastify.listen({
host: '0.0.0.0',
port: 8081,
})
}
start().catch(err => {
console.error(err)
process.exit(1)
})
Fastify has a concept of plugins. In Fastify, everything is a plugin, including routes and middleware.
Once we get more comfortable with using fastify, we can understand what plugins in fastify actually mean and the value they provide.
The line await fastify.register(App)
in the code above is creating a scope where all the logic written in app.ts
is contained.
To start the application, you can use:
pnpm dev
And if you request the endpoint with curl:
curl http://0.0.0.0:8081/movies
You should receive a response with "Movies listed successfully," even though the list is currently empty.
Now, let's create a POST endpoint.
POST Request
In app.ts
, add the following code to our previous function.
import Sensible from '@fastify/sensible'
// Favmovies interface and variable
export default async function(fastify: FastifyInstance, opts: FastifyPluginOptions){
await fastify.register(Sensible)
// GET /movies endpoint
fastify.route({
url: '/movies',
method: 'POST',
handler: function handler(request, reply) {
const data = request.body as FavMovies
if (!data?.title || !data?.description || !data.genre) {
throw fastify.httpErrors.badRequest(
'Please ensure all information, title, description and genre are provided',
)
}
favMovies.push({
title: data.title,
description: data.description,
genre: data.genre,
})
reply.send({
message: 'Movie added succesfully',
success: true,
data: null,
})
},
})
}
Now, let's go through the code above:
We've added a new endpoint using the route
method. In this example, we perform simple validation and throw an error when it fails.
To make it easier to throw an error, we use a Fastify plugin called Sensible, which we installed in Part I. Sensible is a community Fastify plugin that provides some helpful utilities. This demonstrates how easily you can add community plugins to extend the features of your Fastify application.
You can test the POST request with curl:
curl --location 'http://0.0.0.0:8081/movies' \
--header 'Content-Type: application/json' \
--data '{
"title": "Pulp fiction",
"description": "A must watch movie!",
"genre": "action"
}'
Shorthand method
Fastify provides a shorthand method for creating API routes. In app.ts
, add the following code to the default function:
fastify.get('/movies/:movieGenre', function getMovie(request, reply) {
const requestParams = request.params as { movieGenre: string }
const searchingFor = requestParams.movieGenre
const result = favMovies.filter(movie => movie.genre === searchingFor)
if (result) {
return {
message: 'Movie info found succesfully',
success: true,
data: result,
}
} else {
throw fastify.httpErrors.notFound(
`Could not find movies with the genre: ${searchingFor}`,
)
}
})
The above code demonstrates an alternative way to write routes. It also shows how the request
object is used to get request parameters. In this case, we are not using the reply
object but simply returning the result from the function, which works the same as using the reply.send
method.
In this part, we’ve explored the fundamental techniques for creating API endpoints in Fastify. Now that we have a solid grasp of how to build basic APIs with Fastify, our journey continues in the next installment. In the upcoming section, we will shift our focus towards the crucial aspect of setting up and integrating a database with our Fastify application, taking our development to the next level.