Hands-on with Deno: Developing REST API + MongoDB + Linux

Hello everyone. This time I decided to do something more interesting than another bot, so next I will show you how to implement the REST API with Deno, connect and use MongoDB as a database, and run it all from under Linux.





A video version of this post is available below:





Description of the task

As an example, I chose the Github Gists API and the following methods:





  • [POST] Create a gist;





  • [GET] List public gists;





  • [GET] Get a gist;





  • [PATCH] Update a gist;





  • [DELETE] Delete a gist.





Project creation

First, we add a file api/mod.ts



:





console.log('hello world');
      
      



And we check that everything works with the command deno run mod.ts



:





mod.ts
mod.ts

Add dependencies

Create a file api/deps.ts



and add the following dependencies:





  • Oak package for working with API;





  • Mongo package for working with MongoDB;





/* REST API */
export { Application, Router } from "<https://deno.land/x/oak/mod.ts>";
export type { RouterContext } from "<https://deno.land/x/oak/mod.ts>";
export { getQuery } from "<https://deno.land/x/oak/helpers.ts>";

/* MongoDB driver */
export { MongoClient, Bson } from "<https://deno.land/x/mongo@v0.21.0/mod.ts>";
      
      



Aside : Unlike NodeJS, the authors of Deno have dropped support for npm and node_modules



, and the necessary libraries are url-linked and cached locally. The libraries themselves can be found in the Third Party Modules section at http://deno.land .





Adding the Boilerplate API

Next, add the code to run the API to the file mod.ts



:





import { Application, Router } from "./deps.ts";

const router = new Router();

router
  .get("/", (context) => {
    context.response.body = "Hello world!";
  });

const app = new Application();

app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: 8000 });
      
      



Application



Router



deps.ts



.





, :





  • deno run --allow-net mod.ts



    ;





  • http://localhost:8000



    ;





  • 'Hello world!';





: Deno secure by default. , () (--allow-net



, (--allow-read



--allow-write



, (--allow-env



) .





POST /gists

, .





:





  • [POST] /gists





  • :





    • content: string | body;





  • :





    • 201 Created;





    • 400 Bad Request;





handlers



create.ts



, handler () :





import { RouterContext } from "../deps.ts";

import { createGist } from "../service.ts";

export async function create(context: RouterContext) {
  if (!context.request.hasBody) {
    context.throw(400, "Bad Request: body is missing");
  }

  const body = context.request.body();
  const { content } = await body.value;
  if (!content) {
    context.throw(400, "Bad Request: content is missing");
  }

  const gist = await createGist(content);

  context.response.body = gist;
  context.response.status = 201;
}
      
      



:





  • (request.hasBody



    !content



    );





  • createGist



    ( );





  • 201 Created.





, ( service.ts



):





import { insertGist } from "./db.ts";

export async function createGist(content: string): Promise<IGist> {
  const values = {
    content,
    created_at: new Date(),
  };

  const _id = await insertGist(values);

  return {
    _id,
    ...values,
  };
}

interface IGist {
  _id: string;
  content: string;
  created_at: Date;
}
      
      



content: string



, IGist



.





MongoDB. db.ts



:





import { Collection } from "<https://deno.land/x/mongo@v0.21.0/src/collection/collection.ts>";
import { Bson, MongoClient } from "./deps.ts";

async function connect(): Promise<Collection<IGistSchema>> {
  const client = new MongoClient();
  await client.connect("mongodb://localhost:27017");
  return client.database("gist_api").collection<IGistSchema>("gists");
}

export async function insertGist(gist: any): Promise<string> {
  const collection = await connect();
  return (await collection.insertOne(gist)).toString();
}

interface IGistSchema {
  _id: { $oid: string };
  content: string;
  created_at: Date;
}
      
      



:





  • MongoDB;





  • gist_api



    connect



    ;





  • , gist_api



    IGistSchema



    ;





  • insertOne



    (inserted id);





MongoDB

, :





sudo systemctl start mongod
sudo systemctl status mongod
      
      



, :





: MongoDB Ubuntu





  • deno run --allow-net mod.ts



    ;





  • Postman API:





, 201 Created



_id



:





: , TypeScript . - Deno TypeScript .





GET /gists

, .





:





  • [GET] /gists





  • :





    • skip: string | query;





    • limit: string | query;





  • :





    • 200 OK;





handlers/list.ts



, handler () :





import { getQuery, RouterContext } from "../deps.ts";

import { getGists } from "../service.ts";

export async function list(context: RouterContext) {
  const { skip, limit } = getQuery(context);

  const gists = await getGists(+skip || 0, +limit || 0);

  context.response.body = gists;
  context.response.status = 200;
}
      
      



:





  • query string getQuery



    ;





  • getGists



    ( );





  • 200 OK;





: number



, string



. +skip || 0



( , NaN



0



).





, :





export function getGists(skip: number, limit: number): Promise<IGist[]> {
  return fetchGists(skip, limit);
}
      
      



skip: number



limit: number



, , IGist



.





MongoDB. fetchGists



db.ts



:





export async function fetchGists(skip: number, limit: number): Promise<any> {
  const collection = await connect();
  return await collection.find().skip(skip).limit(limit).toArray();
}
      
      



:





  • gist_api



    connect



    ;





  • , skip



    - limit



    ;





  • deno run --allow-net mod.ts



    ;





  • Postman API:





, 200 OK



:





GET /gists/:id

.





:





  • [GET] /gists/:id





  • :





    • id: string | path





  • :





    • 200 OK;





    • 400 Bad Request;





    • 404 Not Found.





handlers/get.ts



, handler () :





import { RouterContext } from "../deps.ts"
import { getGist } from "../service.ts";

export async function get(context: RouterContext) {
    const { id } = context.params;
    if(!id) {
        context.throw(400, "Bad Request: id is missing");
    }

    const gist = await getGist(id);
    if(!gist) {
        context.throw(404, "Not Found: the gist is missing");
    }

    context.response.body = gist;
    context.response.status = 200;
}
      
      



:





  • id



    400 ;





  • getGist



    404 ( );





  • 200 OK;





, :





export function getGist(id: string): Promise<IGist> {
    return fetchGist(id);
}

interface IGist {
  _id: string;
  content: string;
  created_at: Date;
}
      
      



id: string



, IGist



.





MongoDB. fetchGist



db.ts



:





export async function fetchGist(id: string): Promise<any> {
  const collection = await connect();
  return await collection.findOne({ _id: new Bson.ObjectId(id) });
}
      
      



:





  • gist_api



    connect



    ;





  • findOne



    _id



    ;





  • deno run --allow-net mod.ts



    ;





  • Postman API:





, 200 OK



:





PATCH /gists/:id

.





, :





  • [PATCH] /gists/:id





  • :





    • id: string | path





    • content: string | body





  • :





    • 200 OK;





    • 400 Bad Request;





    • 404 Not Found.





handlers/update.ts



, handler () :





import { RouterContext } from "../deps.ts";

import { getGist, patchGist } from "../service.ts";

export async function update(context: RouterContext) {
  const { id } = context.params;
  if (!id) {
    context.throw(400, "Bad Request: id is missing");
  }

  const body = context.request.body();
  const { content } = await body.value;
  if (!content) {
    context.throw(400, "Bad Request: content is missing");
  }

  const gist = await getGist(id);
  if (!gist) {
    context.throw(404, "Not Found: the gist is missing");
  }

  await patchGist(id, content);

  context.response.status = 200;
}
      
      



:





  • id



    400 ;





  • !content



    ;





  • getGist



    404 ;





  • patchGist



    ( );





  • 200 OK.





, :





export async function patchGist(id: string, content: string): Promise<any> {
  return updateGist({ id, content });
}

interface IGist {
  _id: string;
  content: string;
  created_at: Date;
}
      
      



id: string



content: string



, any



.





MongoDB. updateGist



db.ts



:





export async function updateGist(gist: any): Promise<any> {
  const collection = await connect();
  const filter = { _id: new Bson.ObjectId(gist.id) };
  const update = { $set: { content: gist.content } };
  return await collection.updateOne(filter, update);
}
      
      



:





  • gist_api



    connect



    ;





  • filter



    , ;





  • update



    , ;





  • updateOne



    ;





  • deno run --allow-net mod.ts



    ;





  • Postman API:





, 200 OK



:





DELETE /gists/:id

, , .





, :





  • [DELETE] /gists/:id





  • :





    • id: string | path





  • :





    • 204 No Content;





    • 404 Not Found.





handlers/remove.ts



, handler () :





import { RouterContext } from "../deps.ts";

import { getGist, removeGist } from "../service.ts";

export async function remove(context: RouterContext) {
  const { id } = context.params;
  if (!id) {
    context.throw(400, "Bad Request: id is missing");
  }

  const gist = await getGist(id);
  if (!gist) {
    context.throw(404, "Not Found: the gist is missing");
  }

  await removeGist(id);

  context.response.status = 204;
}
      
      



:





  • id



    400 ;





  • getGist



    404 ;





  • removeGist



    ( );





  • 204 No Content.





, :





export function removeGist(id: string): Promise<number> {
  return deleteGist(id);
}
      
      



id: string



number



.





MongoDB. deleteGist



db.ts



:





export async function deleteGist(id: string): Promise<any> {
  const collection = await connect();
  return await collection.deleteOne({ _id: new Bson.ObjectId(id) });
}
      
      



:





  • gist_api



    connect



    ;





  • deleteOne



    _id



    ;





  • deno run --allow-net mod.ts



    ;





  • Postman API:





, 204 No Content



:





: . isDeleted: boolean



.





FAQ

API 404 Not Found

router



mod.ts



:





import { Application, Router } from "./deps.ts";
import { list } from "./handlers/list.ts";
import { create } from "./handlers/create.ts";
import { remove } from "./handlers/remove.ts";
import { get } from "./handlers/get.ts";
import { update } from "./handlers/update.ts";

const app = new Application();

const router = new Router();

router
  .post("/gists", create)
  .get("/gists", list)
  .get("/gists/:id", get)
  .delete("/gists/:id", remove)
  .patch("/gists/:id", update);

app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: 8000 });

      
      



API 500 Internal Server Error

:





const app = new Application();

app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    console.log(err);
  }
});

...
      
      



  • ;





  • GitHub Gist API ;





  • API;





  • MongoDB;





  • MongoDB Ubuntu.





.





, , , , -.












All Articles