Një udhëzues i shpejtë për krijimin e një API të vërtetuar të dokerizuar me Koa. Kjo mund të përdoret si bazë për të krijuar çdo API që ju pëlqen, nga aplikacionet e motit deri te menaxhimi i restoranteve. Mund të lidhet lehtësisht me çdo frontend, për të krijuar një aplikacion funksional në internet. Ndërsa nuk do të shtohet shumë funksionalitet në këtë udhëzues, ai do të përfundojë të gjithë konfigurimin e nevojshëm për të nisur aplikacionin tuaj! Për pllakën e plotë të bojlerit, shikoni këtë depo GitHub: https://github.com/oflint-1/koa-auth-boilerplate.

Kushtet paraprake:

  • Npm dhe nyja
  • Doker

Teknologjitë e përdorura:

  • KoaJS (korniza Javascript e përdorur për të krijuar API)
  • Pasaporta (Ofron vërtetimin)
  • MongoDB + Mongoose (Kjo do të jetë baza e të dhënave për API)
  • Docker + DockerCompose (Për të lidhur pjesë të ndryshme të aplikacionit)

Duke filluar

Së pari, krijoni një dosje të re dhe inicializoni npm

mkdir koa-api && cd koa-api

npm init -y

Kjo do të krijojë një skedar bazë package.json dhe do të konfigurojë mjedisin tuaj të nyjes.

Më pas, instaloni paketat fillestare për konfigurimin bazë të Koa.

npm i koa koa-bodyparser koa-router @koa/cors koa-session

Tani mund të fillojmë të krijojmë bazat e aplikacionit tonë. Krijo një skedar të ri të quajtur app.js

/* app.js */

// Imports
const Koa = require("koa");
const Router = require("koa-router"); // Import routing
const bodyParser = require("koa-bodyparser"); // Imports request parser
const cors = require("@koa/cors");
const session = require("koa-session"); // Import authentication sessions

// Create a new Koa app
const app = new Koa();

// Setup default configuration
app.use(cors({ credentials: true }));
app.keys = ["super-secret-key"]; // Sets application key (Make secure for production)
app.use(session(app)); // Tells app to use sessions

// Initialise request parser
app.use(bodyParser());

// Listen on port 3000
app.listen(3000);

Kjo do të krijojë infrastrukturën bazë për projektin tonë. Ekzekutoni node app.js dhe lundroni te localhost:3000 në shfletuesin tuaj të preferuar të internetit dhe duhet të shihni që serveri juaj i uebit po funksionon. Aktualisht kjo duhet të shfaqë një mesazh Not found pasi ne nuk kemi shtuar ende asnjë rrugë në aplikacionin tonë.

Vendosni rrugët bazë

Tani, ne duam të krijojmë rrugët tona bazë API.

Krijo një skedar routes/index.js

/* routes/index.js */

// Setup basic routes
module.exports = (router) => {
  // Set prefix for all routes
  router.prefix("/api");
  // Include api routes
  router.use("", require("./api"));
};

Kjo do të konfigurojë ruterin bazë. Tani duhet të konfigurojmë rrugët API.

Më pas, krijoni skedarin routes/api.js.

/* routes/api.js */

// Import and create router
const Router = require("koa-router");
const router = new Router();

// GET basic route
router.get("/", (ctx, next) => {
  ctx.body = "Hello Api!";
});

// Export router
module.exports = router.routes();

Kjo do të konfigurojë një rrugë fillestare dhe do ta shtojë atë në ruter.

Më pas, shtoni në app.js pas analizuesit të trupit.

/* app.js */
...
// Add routes
const router = new Router(); // Create new router
require("./routes")(router); // Require external routes and pass in the router
app.use(router.routes()); // Use all routes
app.use(router.allowedMethods()); // Setup allowed methods
...

Tani ne mund të shohim rrugën tonë bazë në veprim! Edhe një herë, ekzekutoni nyjen app.js dhe vizitoni localhost:3000/api/ dhe do të shihni një mesazh që thotë Hello Api!

Vendosja e bazës së të dhënave me docker

Më pas do të lidhim API-në tonë me një shërbim të bazës së të dhënave. Ne do të lidhim aplikacionin tonë dhe do ta lidhim atë me mongoDB. Për të filluar, sigurohuni që docker është instaluar dhe funksionon në sistemin tuaj. Shihni: https://docs.docker.com/engine/install/

Krijoni një skedar të ri në dosjen rrënjë të aplikacionit tuaj të quajtur dockerfile

FROM node

WORKDIR /app

COPY package.json /app

COPY package-lock.json /app

RUN npm install

RUN npm install -g nodemon

COPY . /app

EXPOSE 3000

CMD ["nodemon", "app.js"]

Kjo do të krijojë një mjedis të ri nyje, do të kopjojë mbi skedarët e paketës dhe do të instalojë paketat e kërkuara. Më pas ekspozon portin e duhur dhe ekzekuton nodemon për një server që ringarkohet kur bëhet ndonjë ndryshim.

Tani do të përdorim docker compose për të lidhur pjesë të ndryshme të API-së sonë.

Krijo një skedar docker-compose.yml, përsëri në dosjen rrënjë:

#docker-compose.yml

version: "2.0"

# Define the services/containers to be run
services:
  koa-auth-api: #name of your service
    build: . # specify the directory of the Dockerfile
    restart: always
    ports:
      - "3000:3000" #specify ports forwarding
    links:
      - database # link this service to the database service
    volumes:
      - .:/app
    depends_on:
      - database
  database: # name of the service
    image: mongo # specify image to build container from
    ports:
      - "27017:27017"
    volumes:
      - /data/db

Kjo do të krijojë bazën e të dhënave dhe kontejnerin API dhe do t'i lidhë ato së bashku. Për ta parë këtë në veprim, dilni nga aplikacioni juaj i nyjes nëse është ende duke u ekzekutuar me CTRL + C dhe ekzekutoni docker-compose up. Do të duhen disa momente për të instaluar dhe krijuar kontejnerët tuaj docker, por më pas duhet të jeni në gjendje ta shikoni aplikacionin njësoj si më parë.

Tani do të lidhemi me bazën e të dhënave nga API-ja jonë. Së pari, instaloni mongoose me npm i mongoose për t'u lidhur me bazën e të dhënave Mongo.

Tani në app.js, importoni mongoose me importet e tjera.

/* app.js */
...
const mongoose = require("mongoose"); // Imports Mongoose which is used to link to MongoDB
...

Tani duhet të lidhemi me bazën e të dhënave. Vendoseni këtë kod pak përpara se të shtojmë analizuesin e trupit në aplikacionin tonë.

/* app.js */
...
// Connect to database
mongoose.connect(`mongodb://database:27017/test`, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
}); // Connects to database
var db = mongoose.connection; // Stores connection
db.on("error", console.error.bind(console, "connection error:")); // Logs any errors
...

Tani kemi lidhjen tonë të bazës së të dhënave, duhet të përdorim Mongoose për të krijuar një model për përdoruesin tonë. Krijo një skedar të ri models/user.js.

/* models/user.js */

// Imports
const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const userSchema = new Schema(
  {
    // Username - must be unique and fit criteria
    username: {
      type: String,
      required: true,
      unique: true,
      minlength: 1,
      maxlength: 20,
    },
    // Password - will be hashed before storage
    password: { type: String, required: true },
  },
  {
    timestamps: true,
  }
);

// Export model
module.exports = mongoose.model("User", userSchema);

Kjo krijon modelin tonë të përdoruesit, i cili do të mbajë të gjithë përdoruesit tanë në bazën e të dhënave. Kjo do të përdoret më vonë për vërtetim.

Duke folur për vërtetimin…

Vendosja e vërtetimit

Tani jemi gati të konfigurojmë vërtetimin përfundimtar. Së pari, instaloni kërkesat.

npm i bcryptjs koa-passport@4 passport-local

Shënim: Duhet të përdorim koa-passport 4 për shkak të një problemi me pasaportën 6. Shihni: https://github.com/jaredhanson/passport/issues/904.

Krijo një skedar auth.js në dosjen e aplikacionit rrënjë për të trajtuar vërtetimin e përdoruesit.

/* auth.js */

// Imports
const passport = require("koa-passport");
const LocalStrategy = require("passport-local").Strategy; // Import strategy
const User = require("./models/user"); // Get user model
const bcrypt = require("bcryptjs"); // Used to encrypt passwords

const options = {};

// Utility functions for user serialization
passport.serializeUser((user, done) => {
  done(null, user.id);
});

passport.deserializeUser((id, done) => {
  User.findById(id, function (err, user) {
    done(err, user);
  });
});

// Setup authentication
passport.use(
  new LocalStrategy(options, (username, password, done) => {
    // Get user from database
    User.findOne({ username: username }, (err, user) => {
      if (!user) return done(null, false);
      // Compare passwords
      if (comparePass(password, user.password)) {
        return done(null, user);
      } else {
        return done(null, false);
      }
    });
  })
);

// Utility function to compare passwords with hashed versions
function comparePass(userPassword, databasePassword) {
  return bcrypt.compareSync(userPassword, databasePassword);
}

Kjo vendos pasaportën për të punuar me KoaJS dhe MongoDB. Ne kemi përcaktuar funksione për serializimin dhe deserializimin e përdoruesve dhe kemi vendosur strategjinë tonë të vërtetimit. Njoftim për të krahasuar fjalëkalimet këtu, ne po përdorim bcrypt për të krahasuar me versionet e hash.

Më pas duhet të konfigurojmë vërtetimin brenda aplikacionit tonë kryesor.

Së pari, importojeni atë në app.js

/* app.js */
...
const passport = require("koa-passport");
...

Më pas, konfiguroni auth-in në app.js përpara lidhjes së bazës së të dhënave

/* app.js */
...
// Setup authentication
require("./auth"); // Fetches auth file functinos
app.use(passport.initialize()); // Intialises passport authentication
app.use(passport.session()); // Initialises passport sessions
...

Tani vërtetimi është konfiguruar, ne duhet të krijojmë rrugë për të hyrë në të.

Krijo skedarin routes/auth.js

/* routes/auth.js */

// Setup router
const Router = require("koa-router");
const router = new Router();
const Ctrl = require("../controllers/auth");

// Define routes
router.get("/", (ctx, next) => {
  ctx.body = "Hello Auth!";
});
router.post("/login", Ctrl.login);
router.post("/signup", Ctrl.signup);
router.get("/status", Ctrl.status);
router.get("/logout", Ctrl.logout);

// Export router
module.exports = router.routes();

Vini re se rrugët janë të gjitha të lidhura me kontrollorët. Ne nuk i kemi krijuar ende këto, kështu që ato nuk do të funksionojnë për momentin.

Shtoni këto rrugë të reja te ruteri në api.js përpara se të eksportoni ruterin.

/* routes/api.js */
...
// Add subroutes to main router
router.use("/auth", require("./auth"));
...

Tani do të krijojmë kontrollues të vërtetimit në mënyrë që rrugët tona të funksionojnë siç synohet. Ky është një skedar mjaft i gjatë, por do të kryejë veprimet themelore të regjistrimit, hyrjes dhe daljes

Krijo skedarin controllers/auth.js

/* controllers/auth.js */

// Imports
const User = require("../models/user");
const passport = require("koa-passport");
const bcrypt = require("bcryptjs");

// Login function
async function login(ctx) {
  return passport.authenticate("local", (err, user, info, status) => {
    if (user) {
      ctx.login(user);
      ctx.redirect("/api/auth/status");
    } else {
      ctx.status = 400;
      ctx.body = { status: "error" };
    }
  })(ctx);
}

// Signup function
async function signup(ctx) {
  console.log(ctx.request.body.username);
  // Generate salt
  const salt = bcrypt.genSaltSync();
  // Generate hash using password and salt
  const hash = bcrypt.hashSync(ctx.request.body.password, salt);

  // Create new user document using username and hash
  const newUser = new User({
    username: ctx.request.body.username,
    password: hash,
  });

  // Save user to database
  const savedUser = await newUser.save().catch((err) => console.log(err));
  console.log(savedUser);

  // If user saved correctly, login user
  if (savedUser) {
    // Authenticate user
    return passport.authenticate("local", (err, user, info, status) => {
      // If user is valid
      if (user) {
        // Login user
        ctx.login(user);
        ctx.redirect("/api/auth/status");
      } else {
        // Return error
        ctx.status = 400;
        ctx.body = { status: "error" };
      }
    })(ctx);
  } else {
    // If no user returned, return a bad request
    ctx.status = 400;
  }
}

// Status function
async function status(ctx) {
  // Check whether user is authenticated
  if (ctx.isAuthenticated()) {
    ctx.body = true;
  } else {
    ctx.body = false;
  }
}

// Logout function
async function logout(ctx) {
  if (ctx.isAuthenticated()) {
    // Logout user
    ctx.logout();
    ctx.status = 200;
    ctx.body = "logged out";
  } else {
    // Throw error
    ctx.body = { success: false };
    ctx.throw(401);
  }
}

// Export functions
module.exports = {
  login,
  signup,
  status,
  logout,
};

Me funksionimin e të gjithë këtyre kontrollorëve, aplikacioni ynë është i plotë! Shikoni URL-të kryesore më poshtë dhe seksionin "Përpara" për të parë se si mund të përdoret kjo.

URL-të kryesore

/api/auth/login - Identifikohu një përdorues. Dërgo emrin e përdoruesit dhe fjalëkalimin duke përdorur JSON në trupin e kërkesës.

/api/auth/signup - Regjistro një përdorues. Dërgo emrin e përdoruesit dhe fjalëkalimin duke përdorur JSON në trupin e kërkesës.

/api/auth/status - Merrni informacion nëse jeni aktualisht i identifikuar.

/api/auth/logout - Dil nga përdoruesi aktual.

/api/ - Rruga bazë për API

Formati i kërkesës

Shumica e kërkesave për pikat fundore të mësipërme duhet të ndjekin formatin e mëposhtëm:

{
	"username": "example_username",
	"password": "example_password"
}

Duke ecur perpara

Për ta provuar atë, drejtohuni në linjën tuaj të komandës dhe ekzekutoni docker-compose up. Me çdo fat, aplikacioni juaj duhet të funksionojë. Për ta testuar këtë, ne mund të përdorim një mjet të tillë si postier. Thjesht bashkëngjitni informacionin e hyrjes në trupin e kërkesës si një objekt JSON dhe gjithçka duhet të funksionojë!

Nga këtu, ju mund të krijoni rrugët, modelet dhe kontrollorët tuaj. Përdorimi i këtij dizajni të modularizuar do të ndihmojë me çdo zhvillim të ardhshëm dhe çdo seksion mund të ndryshohet individualisht. Kjo mund të përdoret si bazë për çdo aplikacion të ardhshëm që kërkon një API të vërtetuar. Për pllakën e plotë të boilerplate, shikoni këtë depo GitHub: https://github.com/oflint-1/koa-auth-boilerplate

Botuar fillimisht në https://enscrybe-blog.netlify.app.