npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@astralsoft/neptune

v0.0.20

Published

Basitce Neptune object-driven rest api frameworku dur.

Downloads

11

Readme

Neptune Nedir?

Basitce Neptune object-driven rest api frameworku dur.

Peki neden Neptune

Neptune giris kaynagim express ve express kullanirkan karsilastigim bazi sorunlar. Bu sorunlar uzerinden gelistirdigim Microp ve micropun yeterince maintain edilebilir olmamasi. Ufak bir yenilik eklemek icin micropu bir kac kez bastan sona tekrar yazdim. Neptune icin ise Micropun evriminin son hali diyebilirim.

Peki nedir bu sorunlar?

  • Expess cok falza 3.parti pakete ihtiyac duyor. Buda node_modules un asiri sismesine, uygulamanin hantal olmasina, hatta ayaga kalkarken cok uyusuk olmasina sebep oluyo. Bu sebeptern oturu yarn vs paket yoneticileri pnp cozumleri ile beraber geliyor ancak node ekosisteminde her paket pnp icin uygun degil.
  • Request bodysi okumayan bir api gordunuz mu? Bir rest api, request bodysini, queryStringleri, vs parse edebilmek icin ekstra bir pakete ihtiyac duymamali. bir apiden bunu yapmasani bekleriz. Evet express bu kadar generic bir task icin bile 3.parti paketlere ihtiyac duyoyor. Ister netlify da deplay edin, ister node uzerinde Neptune her turlu request boundrysini parse edebilir.

  • Expressi serverless platformlarda deploy edemezsiniz. Buna karsin Neptune platform agnostictir.

  • yil 2022 Express halen promise desteklemiyor. Ister generator dondurun ister promise neptun hepsini kendi basina handle edebilir.

  • Neptune expressin (Model view controller router) yapisi yerine Resource,Service ve Provider kullanir.

  • Neptune XSRF, CSRF gibi bilindik tehtitler icin ekstra pakete ihtiyac duymaz, Zamanla guvenlik suitini gelistirmeye devam edecez.


  • Neptunun tek eskisine gelecek olursak, NodeAdaptoru node 18~ e ihtiyac duyuyor

Neptuneun kullanimi

Neptune app

Bos bir neptune uygulamasi olusturmak bu kadar kolay

import { createNeptune } from "@.../neptune/app";

export const app = createNeptune({
  adapter: NeptuneNodeAdapter,
  hostname: "localhost",
  port: 3000,
  resources: [],
}).run();

Neptune resource

Her neptune resource uygulamamizin bir route'unu temsil ediyor

Bir users resource umuz var diyelim. /users e dusen her istek bu resource da karsilaniyor.

Resource basitce bir class NeptunResource abstract classini extend ediyor.

// userResource.ts
import { NeptunResource } from "@.../common";

export class UsersResource extends NeptuneResource {
  // bu bir array de olabilir ["/users" , "/user"]
  // bu durumda hem /users e hemde /user e atilan istek burada karsilanir
  public path = "/users";

  GET() {
    const users = ["jdoe", "mdoe", "bdoe"]; // dummy users
    return Response.json(users, 200, { "Content-Type": "application/json" });
  }
}
import { NeptuneNodeAdapter } from "@.../neptune/adapter";
import { createNeptune } from "@.../neptune/app";
import { UserResource } from "./path/to/userResource";
export const app = createNeptune({
  adapter: NeptuneNodeAdapter,
  hostname: "localhost",
  port: 3000,
  resources: [UserResource],
}).run();

Gordugunuz gibi expressin her isi yapan bir routeri yerine bir isi cok iyi yapan bir resource ve her resource butun http methodlarini handle edebilir ( get, post, put, patch, delete, head, options, connect, trace). Bu sayede dahada moduler bir yapiya kavusmus oluyoruz.

Body parsing

Neptune body parse edebilmek icin 3.parti paketlere ihtiyac durmaz. Her turlu boundaryi parse edebilir

//upload.ts
import { NeptunResource } from "@.../common";
import type { NeptuneFormData, NeptuneFile } from "@.../internal";

export class UploadResource extends NeptuneResource {
  public path = "/user/upload";

  // body parse etmek asenkron bir islem oldugundan async await kullanacam

  async POST(request: NeptuneRequest) {
    const data: NeptuneFormData = await request.formData();

    const profilePhoto: NeptuneFile = data.get("profile-photo");
    //content type'i text/... haricinde olan hersey icin bize aslinda Reqdable stream olarak donuyor

    try {
      await createWriteStream("kaydedilecek/konum/resim.png").pipe(
        profilePhoto
      );
      return Response.null(200);
    } catch {
      return Response.null(400);
    }
  }
}

NeptuneRequest objesinin body parsing icin sahip oldugu methodlar Text(), Json(), Blob(), FormData(), Buffer(), Xml(), Query()

Sending Response Body

Neptune kendi icinde her turlu datayi serialize edebilir

import { NeptunResource } from "@.../common";
import { createReadStream } from "fs";

class DownloadResource extends NeptuneResource {
  public path = ["/download", "icon.ico"];

  async GET(request: NeptuneRequest) {
    if (request.path !== "icon.ico") {
      // Dosya stream olarak gonderildi.
      return createReadStream("gonderilecek/dosya.png").pipe(
        Response.head({
          "Content-Type": "img/png",
          "Content-Disposition": 'attachment; inline; filename="filename.jpg"',
        })
      );
    }

    // NeptuneResponse .File() methodunada sahip ancak bu buyuk dosyaladarda memory sorunlarina neden olabilir
    return await Response.File("icon.ico", 200, {
      "Content-Type": "img/png",
      "Content-Disposition": 'attachment; inline; filename="filename.jpg"',
    });
  }
}

Request parametresi almak

neptune /foo/bar , /foo/:bar , /\/foo\/[a-z]+/, /foo/[a-z] seklindeki parametrelerin tumunu destekler

export class UserResource extends NeptuneResource {
  public path = "/user/:id";

  GET() {
    const users = ["jdoe", "mdoe", "bdoe"]; // dummy users

    // this.param("id") => "1"
    // this.params() => {id: "1"}
    return Response.Text(users[this.param("id") as number]);
  }
}

/user/1 e Get istegi atildigini varsayalim

aldigimiz cevap "jdoe" olacaktir

Error handling

Neptune error handling icin cesitli imkanlar sunar. Bunlardan biri NeptuneError yardimci classi

//userNotFoundError.ts
import { NeptuneError } from from "@.../common";

export class UserNotFoundError extends NeptuneError {
    message = "User not found";
    status = 404
}
import { NeptuneResource } from from "@.../common";
import type { INeptuneError } from from "@.../common";
import { UserNotFoundError } from "../path/to/userNotFoundError"
export class UserResource extends NeptuneResource {
  public path = "/user/:id";

  GET() {
    const users = ["jdoe", "mdoe", "bdoe"]; // dummy users


    if(!users[this.param("id")]) throw new UserNotFoundError()

    return Response.Text(users[this.param("id") as number]);
  }


  /*
    Resource icinde her hangi bir error firlatilirsa bu endpointe duser
    Burada gonderilecek responsa dair herseyi override edebilirsiniz
    Bu eger NeptuneError dan turetilmis bir class firlatildi ise bu endpoint zorunlu degildir ve tanimlanmadigi taktirde otomatik olarak Error sinifi donecektir
    {
        status: 404,
        body: {
            message: "User not found"
        }
    }

  */
    ERROR(error: INeptuneError ) {

        if(error instanceof UserNotFoundError) {
                return  this.setHeader("x-user", "not found")
        }

        else return Response.null(500)
    }
}

/user/1 e istek atildigini varsayalim

gelecek response su sekilde olacak

{
  "status": 200,
  "body": "mdoe",
  "headers": {...}
}

eger /user/4 e istek atmis olsaydik

{
    "status": 404,
    "body" : { "message" : "User not found"},
    "headers": {
        ...,
        "x-user": "not found"
    }
}

Gelecek veri tipini zorunlu tutma

Bunun icin NeptuneInput yardimci class indan turetilmis bir interface yada class kullanmak yeterli yeterli

    import { NeptuneInput } from from "@.../common";
    // sadece typescript
    export interface ILoginInput extends NeptuneInput {
        password: string;
        username: string;
    }

    // yada

    export class LoginInput extends NeptuneInput {
        password: string;
        username: string;
    }

Generic kullanmak sadece Typescript

import { NeptuneResource } from from "@.../common";
import { ILoginInput } from "../path/to/loginInput";
export class LoginResource extends NeptuneResource {
  public path = "/login";

  async POST(request) {

    const { password, username } = await requset.json<IUserInput>()
    // eger istenilen input gelirse sorunsuz bir sekilde ilerleyecek
    // gelmezse ise InputIsNotValid { error : InputNotValidError, message: "<T> InputNotValid"} hatasi firlatilacak, ERROR handlerinda karsilayabilirsiniz


    return Response.Text("Wellcome user");
  }

}

Declarative yontem

import { NeptuneResource } from from "@.../common";
import { ILoginInput } from "../path/to/loginInput";
export class LoginResource extends NeptuneResource {
  public path = "/login";

  // POST methoduna gelen body inputa uygun olmazsa ayni process buradada isleyecek
  input = {
    POST: ILoginInput
  }

  async POST(request) {

    const { password, username } = await requset.json()



    return Response.Text("Wellcome user");
  }

}

Servisler

Servisleri Express middleware leri gibi dusune bilirsiniz. Ancak dahada fazlasi var

Servislerde basitce bir class NeptuneService abstract classindan turetiliyor


//authService.ts
import { NeptuneService } from from "@.../common";


class AuthService extends NeptuneService {

    runBeforeResource(request) {

        const token = this.cookies("token")

        if(!token) throw new IsNotAuthError() // dummy authError classi

        const verify = jwt.verify(token) // dummy jwt :D

        if(!token) throw new TokenIsNotValidError() // dummy tokenNotValidError classi


        // Keyleri string tipinde olan her hangi bir obje donebilirsiniz buna daha sonra gelecez
        return {
            session: verify
        }
    }


    // eger ERROR methodu service icinde tanimlandiysa mutlaka response donmelidir.
    // tanimlanmadi ise Resource daki error methodu calisacaktir. eger o da yoksada error classina gore response donecektir
    ERROR (error) {
        Response.null(401)
    }
}

Soyle bir seneryomuz olsun, kullanici addressini degistirmek istiyor diyelim. Bu resourca gelmeden once giris yapip yapmadigini kontrol ediyoruz. Eger giris yapti ise buradaki pathc methodu calissin yoksa 401 gonderip istegi sonlandirsin

//...imports

export class AddressResource extends NeptuneResource {
  public path = "/user/address";

  // bu resourca HER PATHC istegi geldiginde pathc dizinindeki her servis sirasi ile calisacak
  services: {
    PATHC: [AuthService] // service ler bir biri ardina chain edilebilir
  }

  async PATCH(request) {

    const address = await requset.<IAddressInput>json()

    // servislerin dondugu her objeye this.locals den ulasabiliriz. servislerin dondugu her locals objesi bir birine merge edilir. ayrica locals objesi servislerde de ulasilabilirdir. bir servisin dondugu locals objesine diger servisten ulasilabilir
    const user = this.locals.session;  // Service den tanidik geldi mi?

    // providerlera servislerden sonra gelecez
    const newAddress = await  (this.providers.getProvider("UserProvider")).getUser(user).setAddress(address)

    return Response.json(newAddress)
  }

}

Providers

Provider lari viewModel gibi dusunebilirsiniz. Providerlar NeptuneProvider abstract classindan turetiliyor. Providerlar Transient, singleton ve scoped olarak calisabilirler ve Neptune de di containerlarinin temelini olusturur

// userProvider.ts
import { NeptuneProvider } from "@.../ioc";

export class UserProvider extends NeptuneProvider {
  scope = "transient"; // "transient" , "singleton" , "scoped"
  name = "UserProvider"; // providerlara ulasmak icin gerekli
  findUserById(id) {
    // fake UserModel
    return UserModel.findById(id);
  }
}
//...imports

export const app = createNeptune({
  adapter: NeptuneNodeAdapter,
  hostname: "localhost",
  port: 3000,
  resources: [UserResource],
  providers: [UserProvider], // providerimizi ekledik
}).run();

providerlara herhangi bir resource, service veya baska bir providerdan erisilebilir

//...imports

export class UserResource extends NeptuneResource {
  public path = "/user/:id";

  async GET(request) {
    // providerlera servislerden sonra gelecez
    // Providerlara ulasmak asenkrondur, sadece cagirildiklarinda inject edilir ve sonrasinda dispose edilir
    const userProvider = await this.providers.getProvider("UserProvider");

    return Response.json<Partial<User>>(
      userProvider.findUserById(this.param("id"))
    );
  }
}