The creator of Node JS, Ryan Dahl, brings up a new technology called Deno, which is an improvement to Node JS. Please note that, Node is not dead – and would still be in existence due to its popularity and ecosystem, but Deno is just a superset of Node and brings in better security and integrates typescript on the fly.
In this post, we’ll be create a simple RESTful API
Benefits of Deno
- No more node_modules, i.e no more, NPM packages or package.json 🙂
- Runtime for JavaScript and Typescript (Inbuilt)
- Global await scope
- Use cache for storing modules globally on your local PC from https://deno.land/x/
- Built-in Testing
- Enhanced Security
- Unique Standard Library
- Modern JavaScript ( using ES Modules)
Install Deno
Deno ships as a single executable with no dependencies. You can install it using the installers below;
Shell (Mac, Linux):
$ curl -fsSL https://deno.land/x/install/install.sh | sh
PowerShell (Windows):
$ iwr https://deno.land/x/install/install.ps1 -useb | iex
Homebrew (Mac):
$ brew install deno
After installing Deno, just run deno in your command prompt.
Incase you come across this error of command not found, find fix below;
Fix zsh: command not found: deno
If you are using Oh My God Sh, then…
$ export PATH="/home/{your username}/.deno/bin:$PATH"
$ source ~/.zshrc
If you are using Bash, then…
$ export PATH="/home/{your username}/.deno/bin:$PATH"
$ source ~/.bashrc
Building a Simple Items API
First, I will be using the VScode text editor with a Deno extension, ensure you have it installed. You can find it here Deno by justjavac
Folder Structure
Setting up the Server
Let’s make a folder and switch into it
$ mkdir deno_artists_api && cd deno_artists_api
Now create a server.ts file in the root directory and paste the code below;
import { Application } from 'https://deno.land/x/oak/mod.ts';
const port = 6000;
const app = new Application();
console.log(`Server is running on port ${port}`);
app.listen({ port });
This will represent our entry to our app. But Deno does not allow a successful startup until the router middleware is added.
Now let’s create a routes.ts file in our root directory to contain our routes
import { Router } from 'https://deno.land/x/oak/mod.ts';
const router = new Router();
export default router;
this above script must be imported into our server.ts file.
import { Application } from 'https://deno.land/x/oak/mod.ts';
// Added
import router from './routes.ts';
const port = 6000;
const app = new Application();
//Added
app.use(router.routes());
app.use(router.allowedMethods());
console.log(`Server is running on port ${port}`);
app.listen({ port });
Okay! so for the run a test, lets add a welcome route to our API
routes.ts file
import { Router } from 'https://deno.land/x/oak/mod.ts';
const router = new Router();
// Added
router.get('/api/v1/welcome', ({ response }: { response: any }) => {
response.body = {
success: true,
message: "Welcome to Artists API"
}
});
export default router;
$ deno run --allow-net server.ts
Now lets test our API’s first route. Open Postman if you use it
Adding Controllers
Now, since we are creating minimal CRUD. I will be using music artists for example,
Let’s build our data, we’ll work with an array of data. Create a data.ts and a types.ts file in the root directory for TypeScript interface of our data.
types.ts file
export interface Artist {
id: string;
name: string;
genre: string;
followers: string;
}
Now the data.ts files
import { Artist } from './types.ts'
export let artists: Artist[] = [
{
id: '1',
name: 'Davido',
genre: 'Afrobeats',
followers: '20m'
},
{
id: '2',
name: 'Chris Brown',
genre: 'Pop',
followers: '65m'
},
{
id: '3',
name: 'Wizkid',
genre: 'Afrobeats',
followers: '11m'
},
{
id: '4',
name: 'Tiwa Savage',
genre: 'Afrobeats',
followers: '10.5m'
},
{
id: '5',
name: 'Yemi Alade',
genre: 'Afrobeats',
followers: '11m'
},
{
id: '6',
name: 'Drake',
genre: 'Hip Hop',
followers: '70m'
},
{
id: '7',
name: '6ix9ine',
genre: 'Hip Hop',
followers: '68m'
},
{
id: '8',
name: 'Lil Baby',
genre: 'Hip Hop',
followers: '15m'
},
{
id: '9',
name: 'Summer Walker',
genre: 'R & B',
followers: '5m'
},
{
id: '10',
name: 'Rihanna',
genre: 'R & B',
followers: '150m'
}
]
The script above shows an array of artists with the Type of Artist we created in types.ts
Now let’s create a file in the Controllers folder called artists.ts and add our first function called getArtists().
import { artists } from '../data.ts';
import { Artist } from '../types.ts';
// @desc Get all Artists
// @route GET /api/v1/artists
const getArtists = ( {response }: { response: any }) =>{
response.body = {
success: true,
message: "Artists retrieved successfully",
data: artists
}
}
export { getArtists }
On routes.ts lets include the route
import { Router } from 'https://deno.land/x/oak/mod.ts';
// Added
import { getArtists } from './controllers/artists.ts'
const router = new Router();
router.get('/api/v1/welcome', ({ response }: { response: any }) => {
response.body = {
success: true,
message: "Welcome to Artists API"
}
});
// Added
router.get('/api/v1/artists', getArtists);
export default router;
$ deno run --allow-net server.ts
Getting a Single Artist
Okay, moving on, still on the Controllers/artists.ts file. let get a Single artist
import { artists } from '../data.ts';
import { Artist } from '../types.ts';
// @desc Get all Artists
// @route GET /api/v1/artists
const getArtists = ( {response }: { response: any }) =>{
response.body = {
success: true,
message: "Artists retrieved successfully",
data: artists
}
}
// Added
// @desc Get an Artists
// @route GET /api/v1/artists/:id
const getSingleArtist = ({ params, response }: { params: {id: string}, response: any }) =>; {
const artist: Artist | undefined = artists.find(artist => artist.id === params.id)
if(artist){
response.status = 200
response.body = {
success: true,
message: "Artist retrieved successfully",
data: artist
}
}else{
response.status = 404
response.body = {
success: false,
data: "Artist not found"
}
}
}
export { getArtists, getSingleArtist }
Lets add the route to our routes.ts
import { Router } from 'https://deno.land/x/oak/mod.ts';
// Updated
import { getArtists, getSingleArtist } from './controllers/artists.ts'
const router = new Router();
router.get('/api/v1/welcome', ({ response }: { response: any }) => {
response.body = {
success: true,
message: "Welcome to Artists API"
}
});
// Updated
router.get('/api/v1/artists', getArtists)
.get('/api/v1/artists/:id', getSingleArtist);
export default router;
Lets restart our server and test it
$ deno run --allow-net server.ts
Adding an Artist
Now, let’s create the function to add an Artist called addArtist() into our artists.ts file in the controller folder.
To generate unique IDs, we’ll use the uuid in the deno Standard Library
// Added
import { v4 } from 'https://deno.land/std/uuid/mod.ts'
import { artists } from '../data.ts';
import { Artist } from '../types.ts';
// @desc Get all Artists
// @route GET /api/v1/artists
const getArtists = ( {response }: { response: any }) =>{
response.body = {
success: true,
message: "Artists retrieved successfully",
data: artists
}
}
// @desc Get an Artists
// @route GET /api/v1/artists/:id
const getSingleArtist = ({ params, response }: { params: {id: string}, response: any }) => {
const artist: Artist | undefined = artists.find(artist => artist.id === params.id)
if(artist){
response.status = 200
response.body = {
success: true,
message: "Artist retrieved successfully",
data: artist
}
}else{
response.status = 404
response.body = {
success: false,
data: "Artist not found"
}
}
}
// Added
// @desc Add an Artists
// @route POST /api/v1/artists/
const addArtist = async ({ request, response }: {request: any, response: any }) => {
const body = await request.body()
if(!request.hasBody){
response.status = 400;
response.body = {
success: false,
message: "Ensure to enter data"
}
}else {
const artist: Artist = body.value
artist.id = v4.generate();
artists.push(artist)
response.status = 201;
response.body = {
success: true,
message: "Artist added successfully",
data: artist
}
}
}
// Updated
export { getArtists, getSingleArtist, addArtist }
Lets update the route in the routes.ts
import { Router } from 'https://deno.land/x/oak/mod.ts';
// Updated
import { getArtists, getSingleArtist, addArtist } from './controllers/artists.ts'
const router = new Router();
router.get('/api/v1/welcome', ({ response }: { response: any }) => {
response.body = {
success: true,
message: "Welcome to Artists API"
}
});
// Updated
router.get('/api/v1/artists', getArtists)
.get('/api/v1/artists/:id', getSingleArtist)
.post('/api/v1/artists', addArtist);
export default router;
Lets restart our server and test it
$ deno run --allow-net server.ts
Updating an Artist
Let us now add an update function, we’ll call it updateArtist() into our artists.ts file in the controller folder.
import { v4 } from 'https://deno.land/std/uuid/mod.ts'
import { artists } from '../data.ts';
import { Artist } from '../types.ts';
// @desc Get all Artists
// @route GET /api/v1/artists
const getArtists = ( {response }: { response: any }) =>{
response.body = {
success: true,
message: "Artists retrieved successfully",
data: artists
}
}
// @desc Get an Artists
// @route GET /api/v1/artists/:id
const getSingleArtist = ({ params, response }: { params: {id: string}, response: any }) => {
const artist: Artist | undefined = artists.find(artist => artist.id === params.id)
if(artist){
response.status = 200
response.body = {
success: true,
message: "Artist retrieved successfully",
data: artist
}
}else{
response.status = 404
response.body = {
success: false,
data: "Artist not found"
}
}
}
// @desc Add an Artists
// @route POST /api/v1/artists/
const addArtist = async ({ request, response }: {request: any, response: any }) => {
const body = await request.body()
if(!request.hasBody){
response.status = 400;
response.body = {
success: false,
message: "Ensure to enter data"
}
}else {
const artist: Artist = body.value
artist.id = v4.generate();
artists.push(artist)
response.status = 201;
response.body = {
success: true,
message: "Artist added successfully",
data: artist
}
}
}
// Added
// @desc update an Artist
// @route PUT /api/v1/artists/:id
const updateArtist = async ({ params, request, response }: { params:{id: string}, request:any, response: any }) => {
const artist: Artist | undefined = artists.find(artist => artist.id === params.id)
if (artist) {
const body = await request.body()
const updateArtist: {name?: string; genre?: string; followers?:string} = body.value
const updatedArtists = artists.map(artist => artist.id === params.id ? {...artist, ...updateArtist} : artist)
response.status = 200;
response.body = {
success: true,
message: "Artist updated successfully",
data: updatedArtists
}
} else {
response.status = 404
response.body = {
success: false,
message: "Artist not found"
}
}
}
// Updated
export { getArtists, getSingleArtist, addArtist, updateArtis }
Lets update the route to our export routes.ts
import { Router } from 'https://deno.land/x/oak/mod.ts';
// Updated
import { getArtists, getSingleArtist, addArtist, updateArtist } from './controllers/artists.ts'
const router = new Router();
router.get('/api/v1/welcome', ({ response }: { response: any }) => {
response.body = {
success: true,
message: "Welcome to Artists API"
}
});
// Updated
router.get('/api/v1/artists', getArtists)
.get('/api/v1/artists/:id', getSingleArtist)
.post('/api/v1/artists', addArtist)
.put('/api/v1/artists/:id', updateArtist);
export default router;
Lets restart our server and test it
$ deno run --allow-net server.ts
Deleting an Artist
Let us now add our delete function called deleteArtist() into our artists.ts file in the controller folder.
import { v4 } from 'https://deno.land/std/uuid/mod.ts'
import { artists } from '../data.ts';
import { Artist } from '../types.ts';
// @desc Get all Artists
// @route GET /api/v1/artists
const getArtists = ( {response }: { response: any }) =>{
response.body = {
success: true,
message: "Artists retrieved successfully",
data: artists
}
}
// @desc Get an Artists
// @route GET /api/v1/artists/:id
const getSingleArtist = ({ params, response }: { params: {id: string}, response: any }) => {
const artist: Artist | undefined = artists.find(artist => artist.id === params.id)
if(artist){
response.status = 200
response.body = {
success: true,
message: "Artist retrieved successfully",
data: artist
}
}else{
response.status = 404
response.body = {
success: false,
data: "Artist not found"
}
}
}
// @desc Add an Artists
// @route POST /api/v1/artists/
const addArtist = async ({ request, response }: {request: any, response: any }) => {
const body = await request.body()
if(!request.hasBody){
response.status = 400;
response.body = {
success: false,
message: "Ensure to enter data"
}
}else {
const artist: Artist = body.value
artist.id = v4.generate();
artists.push(artist)
response.status = 201;
response.body = {
success: true,
message: "Artist added successfully",
data: artist
}
}
}
// @desc update an Artist
// @route PUT /api/v1/artists/:id
const updateArtist = async ({ params, request, response }: { params:{id: string}, request:any, response: any }) => {
const artist: Artist | undefined = artists.find(artist => artist.id === params.id)
if (artist) {
const body = await request.body()
const updateArtist: {name?: string; genre?: string; followers?:string} = body.value
const updatedArtists = artists.map(artist => artist.id === params.id ? {...artist, ...updateArtist} : artist)
response.status = 200;
response.body = {
success: true,
message: "Artist updated successfully",
data: updatedArtists
}
} else {
response.status = 404
response.body = {
success: false,
message: "Artist not found"
}
}
}
// Added
// @desc Remove an Artist
// @route DELETE /api/v1/artists/:id
const deleteArtist = ({ params, response }: { params: {id:string}, response: any }) => {
const artist = artists.filter(artist => artist.id !== params.id)
if (artist) {
response.status = 200
response.body = {
success: true,
message: "Artist removed successfully",
data: artist
}
} else {
response.status = 404
response.body = {
success: false,
message: "Artist not found"
}
}
}
// Updated
export { getArtists, getSingleArtist, addArtist, updateArtist, deleteArtist }
Lets update the route to our export routes.ts
import { Router } from 'https://deno.land/x/oak/mod.ts';
// Updated
import { getArtists, getSingleArtist, addArtist, updateArtist } from './controllers/artists.ts'
const router = new Router();
router.get('/api/v1/welcome', ({ response }: { response: any }) => {
response.body = {
success: true,
message: "Welcome to Artists API"
}
});
// Updated
router.get('/api/v1/artists', getArtists)
.get('/api/v1/artists/:id', getSingleArtist)
.post('/api/v1/artists', addArtist)
.put('/api/v1/artists/:id', updateArtist)
.delete('/api/v1/artists/:id', deleteArtist);
export default router;
Lets restart our server and test it
$ deno run --allow-net server.ts
Find the code in this GitHub Repo
Summary
We have successfully built our first Deno API using Artists records as an Example. In the future, we would be integrating a Database to store our data.
Do not hesitate to point out any corrections you notice. thank you!
Continue to Part 2
Leave a Reply