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

@written/app

v0.5.4

Published

Express-alike Server Framework for Appwrite

Downloads

34

Readme

Written Logo

@written/app

Middleware and Routing Provider for Appwrite Functions.

Codeberg License

Installation

Automatic

It's recommende to use @written/create-function to create a new function, as it will install this package for you. This, however, depends on a repo created with create-written.

Manual

Run pnpm i @written/app in your function.

Usage

import App, { ServerMode } from '@written/app';

const app = new App() // Create the app
  .cors(); // Add CORS support - this will enable CORS for all requests to here, allowing anyone to access it

// This just handles getting /a
app.get('/a', ({ res }) =>
  res.json({
    success: true,
    message: 'Welcome to GET /a',
  }),
);

// This will log a welcome message and set a local
app.post('/a', ({ log, next, locals }) => {
  log('Welcome to @written/app!');
  locals.hello = 'world';
  return next();
});
// This posts to /a
app.post('/a', ({ res, locals }) =>
  res.json({
    success: 1,
    hello: locals.hello,
  }),
);

// This method sets all status codes to 400 if it's a POST and gets to this point (ie is not /a in this app)
app.post(
  null, // Null = serve all routes
  async ({ res, next }) => ({
    ...(await next()), // Get the responses of everything down the chain - simply returning this will be equivalent to just calling next() in express.
    statusCode: 400, // Set the statusCode to 400
  }),
);

// Due to the above, this will 4xx, however it will, for /testing/a, return {success:true,message:'a'} - As we use https://npmjs.com/package/route-parser for route parsing, you can also do this for query params if desired
app.post<{
  test: string;
}>('/testing/:test', ({ params, res }) =>
  res.json({
    success: true,
    message: params.test,
  }),
);

// This method handles providing a 404 to anyone getting to here
app.use(({ res }) =>
  res.json(
    {
      success: false,
      message: 'ENOENT - Route not found with this given method.',
    },
    404, // This will be 400 when POSTing due to the aforementioned
  ),
);

export default app.server(ServerMode.Appwrite);

Trailing Slashes

Whether or not to have trailing slashes is always a headache - @written/app simplifies this by adding options to either strip, or append trailing slashes as-needed, before the query parameters. This is done through @written/trailing-slash, a package for handling trailing slashes.

In your application

By default, we don't touch the route we pass to your app. However, we do have middleware built-in that can modify the property appropriately. It will only modify req.path, not req.url.

You can use said middleware by calling app.use(App.trailingSlash(Trailing.Mode.[Always|Never])) where App is the static class, not an instance thereof, and Trailing is the class export of @written/trailing-slash.

You can also call app.trailingSlash(Trailing.Mode.[Always|Never]), a short-hand for the above.

It's worth noting that you can add these middleware multiple times at different points in your code, and they'll execute in the order you provide; e.g. if you first do Never, have some middleware, then do Always, and have some more, the middleware called after Never, and before Always, will have it's trailing slashes stripped. The inverse will then apply afterwards.

This does not affect routing; to affect how routing trailing slashes are handled, see the next section.

In Routing

You can change the behaviour of the router's trailing slash handler by changing app.config.routingTrailingSlashes. Note that this option only affects the router, and not what's passed to your app. Usually, the only reason you'd want to change this is to change it to Trailing.Mode.Ignore, as to not touch trailing slashes - ie if you're handling them yourself.

Note: Changing this value after adding any routed middleware will cause said middleware to no longer work. We call the stripping function on the route as it's being added.

Making Test Casess

You can make testcases for this by using @written/mkctx. An example for this being done in jest can be found here:

// app.ts
import App, { ServerMode } from '@written/app';

const app = new App().cors();

app.get('/', ({ res }) => res.send('Hello, World!'));

export default app.server(ServerMode.Appwrite);
// app.test.ts
import { AppwriteResponseObject } from '@written/appwrite-types';
import server from './app';
import Ctx from '@written/mkctx';

describe('MyApp', () => {
  // Create a function for fetch()ing
  let fetch = (requestInfo: string, requestInit: RequestInit = {}) =>
    server(new Ctx(`https://example.com${requestInfo}`, requestInit));
  // Check to make sure / returns an HTTP 200
  it('should return Hello, World! on the index route', async () => {
    expect((await fetch('/')).statusCode).toEqual(200);
  });
  // Check to make sure / returns hello world
  it('should return Hello, World! on the index route', async () => {
    expect((await fetch('/')).body).toEqual('Hello, World!');
  });
});