Giter VIP home page Giter VIP logo

pogo's Introduction


Pogo logo with a dinosaur on a pogo stick

pogo Build status for Pogo TypeScript documentation for Pogo

Server framework for Deno

Pogo is an easy-to-use, safe, and expressive framework for writing web servers and applications. It is inspired by hapi.

Documentation

Supports Deno v1.20.0 and higher.

Contents

Why?

  • Designed to encourage reliable and testable applications.
  • Routes are simple, pure functions that return values directly.
  • Automatic JSON responses from objects.
  • Built-in support for React and JSX.

Usage

Save the code below to a file named server.js and run it with a command like deno run --allow-net server.js. Then visit http://localhost:3000 in your browser and you should see "Hello, world!" on the page. To make the server publicly accessible from other machines, add hostname : '0.0.0.0' to the options.

import pogo from 'https://deno.land/x/pogo/main.ts';

const server = pogo.server({ port : 3000 });

server.router.get('/', () => {
    return 'Hello, world!';
});

server.start();

The examples that follow will build on this to add more capabilities to the server. Some advanced features may require additional permission flags or different file extensions. If you get stuck or need more concrete examples, be sure to check out the example projects.

Adding routes

A route matches an incoming request to a handler function that creates a response

Adding routes is easy, just call server.route() and pass it a single route or an array of routes. You can call server.route() multiple times. You can even chain calls to server.route(), because it returns the server instance.

Add routes in any order you want to, it’s safe! Pogo orders them internally by specificity, such that their order of precedence is stable and predictable and avoids ambiguity or conflicts.

server.route({ method : 'GET', path : '/hi', handler : () => 'Hello!' });
server.route({ method : 'GET', path : '/bye', handler : () => 'Goodbye!' });
server
    .route({ method : 'GET', path : '/hi', handler : () => 'Hello!' })
    .route({ method : 'GET', path : '/bye', handler : () => 'Goodbye!' });
server.route([
    { method : 'GET', path : '/hi', handler : () => 'Hello!' },
    { method : 'GET', path : '/bye', handler : () => 'Goodbye!' }
]);

You can also configure the route to handle multiple methods by using an array, or '*' to handle all possible methods.

server.route({ method : ['GET', 'POST'], path : '/hi', handler : () => 'Hello!' });
server.route({ method : '*', path : '/hi', handler : () => 'Hello!' });

Serve static files

Using h.directory() (recommended)

You can use h.directory() to send any file within a directory based on the request path.

server.router.get('/movies/{file*}', (request, h) => {
    return h.directory('movies');
});

Using h.file()

You can use h.file() to send a specific file. It will read the file, wrap the contents in a Response, and automatically set the correct Content-Type header. It also has a security feature that prevents path traversal attacks, so it is safe to set the path dynamically (e.g. based on the request URL).

server.router.get('/', (request, h) => {
    return h.file('dogs.jpg');
});

Using byte arrays, streams, etc.

If you need more control over how the file is read, there are also more low level ways to send a file, as shown below. However, you’ll need to set the content type manually. Also, be sure to not set the path based on an untrusted source, otherwise you may create a path traversal vulnerability. As always, but especially when using any of these low level approaches, we strongly recommend setting Deno’s read permission to a particular file or directory, e.g. --allow-read='.', to limit the risk of such attacks.

Using Deno.readFile() to get the data as an array of bytes:

server.router.get('/', async (request, h) => {
    const buffer = await Deno.readFile('./dogs.jpg');
    return h.response(buffer).type('image/jpeg');
});

Using Deno.open() to get the data as a stream to improve latency and memory usage:

server.router.get('/', async (request, h) => {
    const file = await Deno.open('./dogs.jpg');
    return h.response(file).type('image/jpeg');
});

💡 Tip: Pogo automatically cleans up the resource (i.e. closes the file descriptor) when the response is sent. So you do not have to call Deno.close()! 🙂

React and JSX support

JSX is a shorthand syntax for JavaScript that looks like HTML and is useful for constructing web pages

You can do webpage templating with React inside of route handlers, using either JSX or React.createElement().

Pogo automatically renders React elements using ReactDOMServer.renderToStaticMarkup() and sends the response as HTML.

Save the code below to a file named server.jsx and run it with a command like deno --allow-net server.jsx. The .jsx extension is important, as it tells Deno to compile the JSX syntax. You can also use TypeScript by using .tsx instead of .jsx.

import React from 'https://esm.sh/react';
import pogo from 'https://deno.land/x/pogo/main.ts';

const server = pogo.server({ port : 3000 });

server.router.get('/', () => {
    return <h1>Hello, world!</h1>;
});

server.start();

Writing tests

Pogo is designed to make testing easy. When you write tests for your app, you will probably want to test your server and route handlers in some way. Pogo encourages pure functional route handlers, enabling them to be tested in isolation from each other and even independently of Pogo itself, with little to no mocking required.

If you want to go further and test the full request lifecycle, you can make actual fetch() requests to the server and assert that the responses have the values you expect. Pogo makes this style of testing easier with server.inject(), which is similar to fetch() except it bypasses the network layer. By injecting a request into the server directly, we can completely avoid the need to find an available port, listen on that port, make HTTP connections, and all of the problems and complexity that arise from networked tests. You should focus on writing your application logic and server.inject() makes that easier. It also makes your tests faster.

When using server.inject(), the server still processes the request using the same code paths that a normal HTTP request goes through, so you can rest assured that your tests are meaningful and realistic.

import pogo from 'https://deno.land/x/pogo/main.ts';
import { assertStrictEquals } from 'https://deno.land/std/testing/asserts.ts';

const { test } = Deno;

test('my app works', async () => {
    const server = pogo.server();
    server.router.get('/', () => {
        return 'Hello, World!';
    });
    const response = await server.inject({
        method : 'GET',
        url    : '/'
    });
    assertStrictEquals(response.status, 200);
    assertStrictEquals(response.headers.get('content-type'), 'text/html; charset=utf-8');
    assertStrictEquals(await response.text(), 'Hello, World!');
});

API

pogo.server(options)

Returns a Server instance, which can then be used to add routes and start listening for requests.

const server = pogo.server();

options

Type: object

catchAll

Type: function

Optional route handler to be used as a fallback for requests that do not match any other route. This overrides the default 404 Not Found behavior built into the framework. Shortcut for server.router.all('/{catchAll*}', catchAll).

const server = pogo.server({
    catchAll(request, h) {
        return h.response('the void').code(404);
    }
});
certFile

Type: string
Example: '/path/to/file.cert'

Optional filepath to an X.509 public key certificate for the server to read when server.start() is called, in order to set up HTTPS. Requires the use of the keyFile option.

hostname

Type: string
Default: 'localhost'

Optional domain or IP address for the server to listen on when server.start() is called. Use '0.0.0.0' to listen on all available addresses, as mentioned in the security documentation.

keyFile

Type: string
Example: '/path/to/file.key'

Optional filepath to a private key for the server to read when server.start() is called, in order to set up HTTPS. Requires the use of the certFile option.

port

Type: number
Example: 3000

Any valid port number (0 to 65535) for the server to listen on when server.start() is called. Use 0 to listen on an available port assigned by the operating system.

pogo.router(options?)

Returns a Router instance, which can then be used to add routes.

const router = pogo.router();

Server

The server object returned by pogo.server() represents your web server. When you start the server, it begins listening for HTTP requests, processes those requests when they are received, and makes the content within each request available to the route handlers that you specify.

server.inject(request)

Performs a request directly to the server without using the network. Useful when writing tests, to avoid conflicts from multiple servers trying to listen on the same port number.

Returns a Promise for a web Response instance.

const response = await server.inject('/foo');
const response = await server.inject(new URL('/foo', server.url));
const response = await server.inject(new Request(new URL('/foo', server.url), { method : 'POST' }));
request

Type: string | URL | Request
Example: '/'

The request info used to determine which route will generate a response. By default, it is a GET request.

server.route(route, options?, handler?)

Adds a route to the server so that the server knows how to respond to requests that match the given HTTP method and URL path. Shortcut for server.router.add().

Returns the server so other methods can be chained.

server.route({ method : 'GET', path : '/', handler : () => 'Hello, World!' });
server.route({ method : 'GET', path : '/' }, () => 'Hello, World!');
server.route('/', { method : 'GET' }, () => 'Hello, World!');
route

Type: object | string | Router | Array<object | string | Router>

method

Type: string | Array<string>
Example: 'GET'

Any valid HTTP method, array of methods, or '*' to match all methods. Used to limit which requests will trigger the route handler.

path

Type: string
Example: '/users/{userId}'

Any valid URL path. Used to limit which requests will trigger the route handler.

Supports path parameters with dynamic values, which can be accessed in the handler as request.params.

handler(request, h)

Type: function

  • request is a Request instance with properties for headers, method, url, and more.
  • h is a Response Toolkit instance, which has utility methods for modifying the response.

The implementation for the route that handles requests. Called when a request is received that matches the method and path specified in the route options.

The handler must return one of the below types or a Promise that resolves to one of these types. An appropriate Content-Type header will be set automatically based on the response body before the response is sent. You can use response.type() to override the default behavior.

Return value Default Content-Type Notes
string text/html An empty string defaults to text/plain, because it cannot be HTML.
JSX element text/html Rendered to static markup with React.
Binary array (ArrayBuffer, Uint8Array, etc.) None Sent as raw bytes.
Binary object (Blob, File, etc.) Uses blob.type Sent as raw bytes.
URLSearchParams application/x-www-form-urlencoded
FormData multipart/form-data
ReadableStream None Streams the body in chunks.
object, number, or boolean application/json The body is stringified and sent as JSON.
Response Uses response.headers
Deno.Reader None Streams the body in chunks.
Error application/json The error is sent as JSON with an appropriate HTTP status code using Bang. By default, a generic error message is used to protect sensitive information. Use Bang directly to send a custom error response. Handlers may either return or throw an error - they are handled the same way.

server.router

Type: Router

The route manager for the server, which contains the routing table for all known routes, as well as various methods for adding routes to the routing table.

server.start()

Begins listening for requests on the hostname and port specified in the server options.

Returns a Promise that resolves when the server is listening see upstream issue denoland/deno_std#2071.

await server.start();
console.log('Listening for requests');

server.stop()

Stops accepting new requests. Any existing requests that are being processed will not be interrupted.

Returns a Promise that resolves when the server has stopped listening.

await server.stop();
console.log('Stopped listening for requests');

Request

The request object passed to route handlers represents an HTTP request that was sent to the server. It is similar to an instance of the web standard Request class, with some additions.

It provides properties and methods for inspecting the content of the request.

request.body

Type: ReadableStream | null

The HTTP body value that was sent in the request, if any.

To get the body as an object parsed from JSON:

server.router.post('/users', async (request) => {
    const user = await request.raw.json();
    // ...
});

To get the body as a string:

server.router.post('/users', async (request) => {
    const user = await request.raw.text();
    // ...
});

For more body parsing methods that are supported, see Request methods.

While using .json() or .text() is convenient and fine in most cases, note that doing so will cause the entire body to be read into memory all at once. For requests with a very large body, it may be preferable to process the body as a stream.

server.router.post('/data', async (request) => {
    if (!request.body) {
        return 'no body';
    }
    for await (const chunk of request.body) {
        const text = new TextDecoder().decode(chunk);
        console.log('text:', text);
    }
    return 'ok';
});

request.headers

Type: Headers

Contains the HTTP headers that were sent in the request, such as Accept, User-Agent, and others.

request.host

Type: string
Example: 'localhost:3000'

The value of the HTTP Host header, which is a combination of the hostname and port at which the server received the request, separated by a : colon. Useful for returning different content depending on which URL your visitors use to access the server. Shortcut for request.url.host.

To get the hostname, which does not include the port number, see request.hostname.

request.hostname

Type: string
Example: 'localhost'

The hostname part of the HTTP Host header. That is, the domain or IP address at which the server received the request, without the port number. Useful for returning different content depending on which URL your visitors use to access the server. Shortcut for request.url.hostname.

To get the host, which includes the port number, see request.host.

request.href

Type: string
Example: 'http://localhost:3000/page.html?query'

The full URL associated with the request, represented as a string. Shortcut for request.url.href.

To get this value as a parsed object instead, use request.url.

request.method

Type: string
Example: 'GET'

The HTTP method associated with the request, such as GET or POST.

request.origin

Type: string
Example: 'http://localhost:3000'

The scheme and host parts of the request URL. Shortcut for request.url.origin.

request.params

Type: object

Contains the name/value pairs of path parameters, where each key is a parameter name from the route path and the value is the corresponding part of the request path. Shortcut for request.route.params.

request.path

Type: string
Example: /page.html

The path part of the request URL, excluding the query. Shortcut for request.url.pathname.

request.raw

Type: Request

The original request object from Deno’s http module, upon which many of the other request properties are based.

💡 Tip: You probably don’t need this, except to read the request body.*

request.referrer

Type: string

The value of the HTTP Referer header, which is useful for determining where the request came from. However, not all user agents send a referrer and the value can be influenced by various mechanisms, such as Referrer-Policy. As such, it is recommended to use the referrer as a hint, rather than relying on it directly.

Note that this property uses the correct spelling of "referrer", unlike the header. It will be an empty string if the header is missing.

request.response

Type: Response

The response that will be sent for the request. To create a new response, see h.response().

request.route

Type: object

The route that is handling the request, as given to server.route(), with the following additional properties:

  • paramNames is an array of path parameter names
  • params is an object with properties for each path parameter, where the key is the parameter name, and the value is the corresponding part of the request path
  • segments is an array of path parts, as in the values separated by / slashes in the route path

request.search

Type: string
Example: '?query'

The query part of the request URL, represented as a string. Shortcut for request.url.search.

To get this value as a parsed object instead, use request.searchParams.

request.searchParams

Type: URLSearchParams

The query part of the request URL, represented as an object that has methods for working with the query parameters. Shortcut for request.url.searchParams.

To get this value as a string instead, use request.search.

request.server

Type: Server

The server that is handling the request.

request.state

Type: object

Contains the name/value pairs of the HTTP Cookie header, which is useful for keeping track of state across requests, e.g. to keep a user logged in.

request.url

Type: URL

The full URL associated with the request, represented as an object that contains properties for various parts of the URL,

To get this value as a string instead, use request.href. In some cases, the URL object itself can be used as if it were a string, because it has a smart .toString() method.

Response

The response object represents an HTTP response to the associated request that is passed to route handlers. You can access it as request.response or create a new response with the Response Toolkit by calling h.response(). It has utility methods that make it easy to modify the headers, status code, and other attributes.

response.body

Type: string | number | boolean | object | ArrayBuffer | TypedArray | Blob | File | FormData | URLSearchParams | ReadaleStream | Deno.Reader | null

The body that will be sent in the response. Can be updated by returning a value from the route handler or by creating a new response with h.response() and giving it a value.

response.code(status)

Sets the response status code. When possible, it is better to use a more specific method instead, such as response.created() or response.redirect().

Returns the response so other methods can be chained.

💡 Tip: Use Deno’s status constants to define the status code.

import { Status as status } from 'https://deno.land/std/http/http_status.ts';
const handler = (request, h) => {
    return h.response().code(status.Teapot);
};

response.created(url?)

Sets the response status to 201 Created and sets the Location header to the value of url, if provided.

Returns the response so other methods can be chained.

response.header(name, value)

Sets a response header. Always replaces any existing header of the same name. Headers are case insensitive.

Returns the response so other methods can be chained.

response.headers

Type: Headers

Contains the HTTP headers that will be sent in the response, such as Location, Vary, and others.

response.location(url)

Sets the Location header on the response to the value of url. When possible, it is better to use a more specific method instead, such as response.created() or response.redirect().

Returns the response so other methods can be chained.

response.permanent()

Only available after calling the response.redirect() method.

Sets the response status to 301 Moved Permanently or 308 Permanent Redirect based on whether the existing status is considered rewritable (see "method handling" on Redirections in HTTP for details).

Returns the response so other methods can be chained.

response.redirect(url)

Sets the response status to 302 Found and sets the Location header to the value of url.

Also causes some new response methods to become available for customizing the redirect behavior:

Returns the response so other methods can be chained.

response.rewritable(isRewritable?)

Only available after calling the response.redirect() method.

Sets the response status to 301 Moved Permanently or 302 Found based on whether the existing status is a permanent or temporary redirect code. If isRewritable is false, then the response status will be set to 307 Temporary Redirect or 308 Permanent Redirect.

Returns the response so other methods can be chained.

response.state(name, value)

Sets the Set-Cookie header to create a cookie with the given name and value. Cookie options can be specified by using an object for value. See Deno’s cookie interface for the available options.

Returns the response so other methods can be chained.

All of the following forms are supported:

response.state('color', 'blue');
response.state('color', { value : 'blue' });
response.state({ name : 'color', value : 'blue' });

It is strongly recommended that you use '__Host-' or '__Secure-' as a prefix for your cookie name, if possible. This will enable additional checks in the browser to ensure that your cookie is secure. Using '__Host-' requires setting the cookie's path option to '/'. See Cookie Name Prefixes for details.

response.state('__Host-session', {
    path  : '/'
    value : '1234'
});

Default cookie options:

Option name Default value
httpOnly true
sameSite 'Strict'
secure true

Note that while sameSite defaults to 'Strict' for security, it causes the cookie to only be sent when the user is already navigating within your site or goes there directly. If instead the user is on another site and follows a link or is redirected to your site, then the cookie will not be sent. Thus, if a logged in user clicks a link to your site from a search engine, for example, it may appear to the user as if they were logged out, until they refresh the page. To improve the user experience for these scenarios, it is common to set sameSite to 'Lax'.

response.state('__Host-session', {
    path     : '/',
    sameSite : 'Lax',
    value    : '1234'
});

response.status

Type: number
Example: 418

The status code that will be sent in the response. Defaults to 200, which means the request succeeded. 4xx and 5xx codes indicate an error.

response.temporary()

Only available after calling the response.redirect() method.

Sets the response status to 302 Found or 307 Temporary Redirect based on whether the existing status is considered rewritable (see "method handling" on Redirections in HTTP for details).

Returns the response so other methods can be chained.

response.type(mediaType)

Sets the Content-Type header on the response to the value of mediaType.

Overrides the media type that is set automatically by the framework.

Returns the response so other methods can be chained.

response.unstate(name)

Sets the Set-Cookie header to clear the cookie given by name.

Returns the response so other methods can be chained.

Response Toolkit

The response toolkit is an object that is passed to route handlers, with utility methods that make it easy to modify the response. For example, you can use it to set headers or a status code.

By convention, this object is assigned to a variable named h in code examples.

h.directory(path, options?)

Creates a new response with a body containing the contents of the directory or file specified by path.

Returns a Promise for the response.

server.router.get('/movies/{file*}', (request, h) => {
    return h.directory('movies');
});

The directory or file that is served is determined by joining the path given to h.directory() with the value of the last path parameter of the route, if any. This allows you to control whether the directory root or files within it will be accessible, by using a particular type of path parameter or lack thereof.

  • A route with path: '/movies' will only serve the directory itself, meaning it will only work if the listing option is enabled (or if the path given to h.directory() is actually a file instead of a directory), otherwise a 403 Forbidden error will be thrown.
  • A route with path: '/movies/{file}' will only serve the directory’s children, meaning that a request to /movies/ will return a 404 Not Found, even if the listing option is enabled.
  • A route with path: '/movies/{file?}' will serve the directory itself and the directory’s children, but not any of the directory’s grandchildren or deeper descendants.
  • A route with path: '/movies/{file*}' will serve the directory itself and any of the directory’s descendants, including children and granchildren.

Note that the name of the path parameter (file in the example above) does not matter, it can be anything, and the name itself won’t affect the directory helper or the response in any way. You should consider it a form of documentation and choose a name that is appropriate and intuitive for your use case. By convention, we usually name it file.

options

Type: object

listing

Type: boolean
Default: false

If true, enables directory listings, so that when the request path matches a directory (as opposed to a file), the response will be an HTML page that shows some info about the directory’s children. including file names, file sizes, and timestamps for when the files were created and modified.

By default, directory listings are disabled for improved privacy, and instead a 403 Forbidden error will be thrown when the request matches a directory.

Note that this option does not affect which files within the directory are accessible. For example, with a route of /movies/{file*} and listing: false, the user could still access /movies/secret.mov if they knew (or were able to guess) that such a file exists. Conversely, with a route of /movies and listing: true, the user would be unable to access /movies/secret.mov or see its contents, but they could see that it exists in the directory listing.

To control which files are accessible, you can change the route path parameter or use h.file() to serve specific files.

h.file(path, options?)

Creates a new response with a body containing the contents of the file specified by path. Automatically sets the Content-Type header based on the file extension.

Returns a Promise for the response.

server.router.get('/', (request, h) => {
    return h.file('index.html');
});
options

Type: object

confine

Type: boolean | string
Default: Deno.cwd() (current working directory)

Optional directory path used to limit which files are allowed to be accessed, which is important in case the file path comes from an untrusted source, such as the request URL. Any file inside of the confine directory will be accessible, but attempting to access any file outside of the confine directory will throw a 403 Forbidden error. Set to false to disable this security feature.

h.redirect(url)

Creates a new response with a redirect status. Shortcut for h.response().redirect(url). See response.redirect() for details.

Returns the response so other methods can be chained.

h.response(body?)

Creates a new response with an optional body. This is the same as returning the body directly from the route handler, but it is useful in order to begin a chain with other response methods.

Returns the response so other methods can be chained.

Router

Documentation: Routing

A router is used to store and lookup routes. The server has a built-in router at server.router, which it uses to match an incoming request to a route handler function that generates a response. You can use the server’s router directly or you can create a custom router with pogo.router().

To copy routes from one router to another, see router.add(). You can pass a custom router to server.route() or server.router.add() to copy its routes into the server’s built-in router, thus making those routes available to incoming requests.

Note that you don’t necessarily need to create a custom router. You only need to create your own router if you prefer the chaining syntax for defining routes and you want to export the routes from a file that doesn’t have access to the server. In other words, a custom router is useful for larger applications.

const server = pogo.server();
server.router
    .get('/', () => {
        return 'Hello, World!';
    })
    .get('/status', () => {
        return 'Everything is swell!';
    });
const router = pogo.router()
    .get('/', () => {
      return 'Hello, World!';
    })
    .get('/status', () => {
      return 'Everything is swell!';
    });

const server = pogo.server();
server.route(router);

router.add(route, options?, handler?)

Adds one or more routes to the routing table, which makes them available for lookup, e.g. by a server trying to match an incoming request to a handler function.

The route argument can be:

  • A route object with optional properties for method, path, and handler
    • method is an HTTP method string or array of strings
    • path is a URL path string
    • handler is a function
  • A string, where it will be used as the path
  • A Router instance, where its routing table will be copied
  • An array of the above types

The options argument can be a route object (same as route) or a function, where it will be used as the handler.

The handler function can be a property of a route object, a property of the options object, or it can be a standalone argument.

Each argument has higher precedence than the previous argument, allowing you to pass in a route but override its handler, for example, by simply passing a handler as the final argument.

Returns the router so other methods can be chained.

const router = pogo.router().add('/', { method : '*' }, () => 'Hello, World!');

router.all(route, options?, handler?)

Shortcut for router.add(), with '*' as the default HTTP method.

Returns the router so other methods can be chained.

const router = pogo.router().all('/', () => 'Hello, World!');

router.delete(route, options?, handler?)

Shortcut for router.add(), with 'DELETE' as the default HTTP method.

Returns the router so other methods can be chained.

const router = pogo.router().delete('/', () => 'Hello, World!');

router.get(route, options?, handler?)

Shortcut for router.add(), with 'GET' as the default HTTP method.

Returns the router so other methods can be chained.

const router = pogo.router().get('/', () => 'Hello, World!');

router.lookup(method, path, host?)

Look up a route that matches the given method, path, and optional host.

Returns the route object with an additional params property that contains path parameter names and values.

router.patch(route, options?, handler?)

Shortcut for router.add(), with 'PATCH' as the default HTTP method.

Returns the router so other methods can be chained.

const router = pogo.router().patch('/', () => 'Hello, World!');

router.post(route, options?, handler?)

Shortcut for router.add(), with 'POST' as the default HTTP method.

Returns the router so other methods can be chained.

const router = pogo.router().post('/', () => 'Hello, World!');

router.put(route, options?, handler?)

Shortcut for router.add(), with 'PUT' as the default HTTP method.

Returns the router so other methods can be chained.

const router = pogo.router().put('/', () => 'Hello, World!');

router.routes

Type: object

The routing table, which contains all of the routes that have been added to the router.

Contributing

See our contributing guidelines for more details.

  1. Fork it.
  2. Make a feature branch: git checkout -b my-new-feature
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin my-new-feature
  5. Submit a pull request.

License

MPL-2.0 © Seth Holladay

Go make something, dang it.

pogo's People

Contributors

afaur avatar danditomaso avatar danrasmuson avatar davidjamesstone avatar hamptonmoore avatar jaspermolgvits avatar nilsnh avatar npup avatar patlehmann1 avatar pkief avatar sholladay avatar tomanagle avatar venikman avatar yereby avatar zpider666 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pogo's Issues

Cannot work example (hello-world) on Deno 0.36.0

I tried to run the following hello world example on Deno 0.36.0.
https://github.com/sholladay/pogo/blob/master/example/hello-world/run.ts

But I got many errors...

Could you please look into this?

Regards,

$ deno run --allow-net --reload /PATH/TO/helloworld.ts
Compile file://PATH/TO/helloworld.ts
Download https://deno.land/x/pogo/main.ts
Download https://deno.land/x/pogo/lib/router.ts
Download https://deno.land/x/pogo/lib/server.ts
Download https://deno.land/x/pogo/dependencies.ts
Download https://deno.land/x/pogo/lib/bang.ts
Download https://deno.land/x/pogo/lib/respond.ts
Download https://deno.land/x/pogo/lib/request.ts
Download https://deno.land/x/pogo/lib/toolkit.ts
Download https://deno.land/[email protected]/http/cookie.ts
Download https://deno.land/[email protected]/http/server.ts
Download https://deno.land/[email protected]/http/http_status.ts
Download https://dev.jspm.io/[email protected]
Download https://dev.jspm.io/[email protected]/server
Download https://deno.land/[email protected]/testing/asserts.ts
Download https://deno.land/[email protected]/datetime/mod.ts
Download https://deno.land/[email protected]/io/bufio.ts
Download https://deno.land/[email protected]/util/async.ts
Download https://deno.land/[email protected]/http/io.ts
Download https://deno.land/[email protected]/strings/mod.ts
Download https://deno.land/[email protected]/io/util.ts
Download https://deno.land/[email protected]/path/mod.ts
Download https://deno.land/[email protected]/path/win32.ts
Download https://deno.land/[email protected]/path/posix.ts
Download https://deno.land/[email protected]/path/constants.ts
Download https://deno.land/[email protected]/path/constants.ts
Download https://deno.land/[email protected]/path/interface.ts
Download https://deno.land/[email protected]/path/glob.ts
Download https://deno.land/[email protected]/path/globrex.ts
Download https://deno.land/[email protected]/path/utils.ts
Download https://deno.land/[email protected]/fmt/colors.ts
Download https://deno.land/[email protected]/testing/diff.ts
Download https://deno.land/[email protected]/testing/format.ts
Download https://deno.land/[email protected]/strings/encode.ts
Download https://deno.land/[email protected]/strings/decode.ts
Download https://deno.land/[email protected]/strings/pad.ts
Download https://deno.land/[email protected]/textproto/mod.ts
Download https://deno.land/x/pogo/lib/response.ts
error TS2339: Property 'next' does not exist on type 'Listener'.

► https://deno.land/[email protected]/http/server.ts:201:49

201     const { value, done } = await this.listener.next();
                                                    ~~~~

error TS7006: Parameter 'message' implicitly has an 'any' type.

► https://deno.land/x/pogo/lib/bang.ts:5:17

5     constructor(message, option = {}) {
                  ~~~~~~~

  :

Found 179 errors.

Binding to Different IP?

Hey so I've tried binding to 0.0.0.0, but it seems like that doesn't work, and when looking through the code, I've noticed that in lib/server.ts#L47, only localhost is set, but the given input by a user for hostname is ignored

Create a logo

Now that Pogo's APIs are stable and we're getting more users, I think it would be nice to add some "fit and finish" in the form of a logo.

Anyone have logo ideas or want to try making one?

Here's some inspiration from my favorite ideas:

The logo should be in a vector format, ideally Adobe Illustrator or SVG.

Add more advanced examples

The examples contained with pogo are very basic.

  • working with request objects should be demonstrated

  • working with path parameters should be demonstrated

    import pogo from 'https://deno.land/x/pogo/main.js';
    server.router.put('/path/{id}', async (request:pogo.Request) => {
        var id:String = request.route.params.id;	
    });
    
  • working with request body should be demonstrated

    import pogo from 'https://deno.land/x/pogo/main.js';
    import { bodyReader } from "https://deno.land/std/http/io.ts";
    server.router.put('/path', async (request:pogo.Request) => {
        const reader = bodyReader(request.raw.contentLength, request.raw.r);
        const bufferContent = await Deno.readAll(reader);
        const bodyText = new TextDecoder().decode(bufferContent);
        const body = JSON.parse(bodyText);	
    });
    
  • etc. (file upload, headers, content types, ..)

Middleware/ext support

It would be good to support some form of middleware to allow processing of requests/responses in a more general fashion. Hapi uses the following (with some extra overloads):

type ExtPoint = "onPreStart" | "onPostStart" | "onPreStop" | "onPostStop"
  | "onRequest" | "onPreAuth" | "onCredentials" | "onPostAuth" | "onPreHandler"
  | "onPostHandler" | "onPreResponse" | "onPostResponse";

interface ExtOptions {
  before?: string | string[];
  after?: string | string[];
  bind?: object;
  sandbox?: string;
  timeout?: number;
}

interface ExtEvent {
  type: ExtPoint;
  method(request: Request, h: Toolkit): void;
  options?: ExtOptions
}

interface Server {
  ext(events: ExtEvent | ExtEvent[]): void;
  ext(
    event: ExtPoint,
    method?: (request: Request, h: Toolkit) => void,
    options?: ExtOptions,
  ): void;
}

A suggestion I had was to use a modified version, which uses class instances for each request. That way you can keep track of information during a request (such as timing), and allows for an API like so:

interface ExtHandler {
  onPreStart?(request: Request): void;
  onPostStart?(request: Request): void;
  onPreStop?(request: Request): void;
  onPostStop?(request: Request): void;
  onRequest?(request: Request): void;
  onPreAuth?(request: Request): void;
  onCredentials?(request: Request): void;
  onPostAuth?(request: Request): void;
  onPreHandler?(request: Request): void;
  onPostHandler?(request: Request, response: Response): void;
  onPreResponse?(request: Request, response: Response): void;
  onPostResponse?(request: Request, response: Response): void;
}

interface Server {
  ext(handler: new (h: Toolkit) => ExtHandler): void;
}

It should be possible to support both, but that would complicate things possibly. Another option would be to support

interface Server {
  ext(handler: ExtHandler): void;
}

in some way to allow for the case where you don't need to store instance data (if this was supported, each method would also get h as well).

CSSType 302 has no location

Pika is no longer returning this package. If you request it, it's a 302 without a location. This causes Rust to panic.

curl the file, and you get a message that you should use this - https://cdn.skypack.dev/csstype

thread 'main' panicked at 'url redirection should provide 'location' header', cli/http_util.rs:141:29
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I can not tell what dependency in Pogo that is requesting this. But I have isolated it as the use of pogo that has caused this.

https://cdn.pika.dev/csstype/dist=es2019,mode=types/index.d.ts

You can see this by running a test with -r to redownload.

The snowpack url does work though. Even though I am not using this feature in pogo, it impacting me. Maybe some of them react support could be a plugin in the future. I have a feeling this may be from the inclusion of react/jsx with pogo.

[RFC] Additional metadata for readme examples?

Might be helpful to link out to the code in readme examples.
Links would use a tag or sha to avoid taking the user to the latest changes.

  • Update: This might introduce a lot of maintenance though, and I am not sure about it.

server.router.get(String, RouteHandler<ServerRequest, Toolkit>)

// Javascript Example
server.router.get('/', (request, { file }) => {
    return file('./foo.jpg');
});
// Typescript Example
server.router.get('/', (request : ServerRequest, { file } : Toolkit) => {
    return file('./foo.jpg');
});

Pre handlers for routes

will 'pre' be implemented or does deno have a better way to order actions that happen within the handler?

Dynamic routes

Is there a way to support dynamic routes?

Something like:

server.route({
  method: 'GET',
  path: '/v2/users/{userId}/books',
  handler: (req, h) => {...}
})

Cannot find name 'HeadersInit' in response.ts

It seems that headers inside the ResponseOptions interface is using a type that is not defined in the file: HeadersInit. I fixed it locally by replacing it with __domTypes.HeadersInit, which is used by the Headers constructor:

const Headers: __domTypes.HeadersConstructor
new (init?: __domTypes.Headers | [string, string][] | Record<string, string> | undefined) => __domTypes.Headers

I also noticed that in the commit history this type was used at one point. Was there a purpose to replacing it? Otherwise I can create a PR with the fix.

TS errors coming from pogo using hello world example

mod.ts

import pogo from 'https://deno.land/x/pogo/main.js';

const server = pogo.server({ port : 3000 });

server.router.get('/', () => {
    return 'Hello, world!';
});

server.start();

deno mod.ts output:

➜  trypogo deno mod.ts    
Compile https://deno.land/[email protected]/http/cookie.ts
error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'.
  Type 'undefined' is not assignable to type 'number'.

► https://deno.land/[email protected]/http/cookie.ts:48:24

48   if (Number.isInteger(cookie.maxAge)) {
                          ~~~~~~~~~~~~~

error TS2532: Object is possibly 'undefined'.

► https://deno.land/[email protected]/http/cookie.ts:49:12

49     assert(cookie.maxAge > 0, "Max-Age must be an integer superior to 0");
              ~~~~~~~~~~~~~

error TS2454: Variable 'req' is used before being assigned.

► https://deno.land/[email protected]/http/server.ts:467:9

467     if (req === Deno.EOF) {
            ~~~

error TS2454: Variable 'req' is used before being assigned.

► https://deno.land/[email protected]/http/server.ts:469:23

469     } else if (err && req) {
                          ~~~

error TS2454: Variable 'slice' is used before being assigned.

► https://deno.land/[email protected]/io/bufio.ts:348:23

348         err.partial = slice;
                          ~~~~~

error TS2454: Variable 'code' is used before being assigned.

► https://deno.land/[email protected]/path/utils.ts:52:30

52     else if (isPathSeparator(code)) break;
                                ~~~~

error TS2454: Variable 'firstPart' is used before being assigned.

► https://deno.land/[email protected]/path/win32.ts:333:10

333   assert(firstPart != null);
             ~~~~~~~~~

error TS2454: Variable 'line' is used before being assigned.

► https://deno.land/[email protected]/textproto/mod.ts:85:52

85         `malformed MIME header initial line: ${str(line)}`
                                                      ~~~~

error TS2454: Variable 'line' is used before being assigned.

► https://deno.land/[email protected]/textproto/mod.ts:145:12

145       if (!line && !more) {
               ~~~~


Found 9 errors.

Deno version:

➜  trypogo deno --version
deno 0.34.0
v8 8.1.108
typescript 3.7.2

Serve a directory of static files

Hi, thanks for doing this @sholladay - is there a way to serve a directory of static files at a particular path with the correct response type set automatically?

The use case is to serve the web UI- e.g. the production build of a create-react-app project

In Hapi, I can do this with @hapi/inert and the directory handler it gives.

  server.route({
    method: "GET",
    path: "/app/{path*}",
    handler: {
      directory: {
        path: Path.join(__dirname, "../../client/build/"),
        listing: false,
        index: true
      }
    }
  });

Add route option for CORS

Hi.

I could not find any examples of the Pogo server / route with enabled CORS.

Here's a couple of examples for a CORS-enabled route:

  1. When using the Toolkit:
server.route({
  handler: (request: Request, tk: Toolkit): Response => {
    const response = tk.response({ info: 'OK' });
    response.headers.append('access-control-allow-origin', '*');
    response.headers.append(
      'access-control-allow-headers',
      'Origin, X-Requested-With, Content-Type, Accept, Range',
    );
    return response.code(200);
  },
  method: 'GET',
  path: '/cors',
});
  1. When using the request.response:
server.route({
  handler: (request: Request, tk: Toolkit): Response => {
    request.response.headers.append('access-control-allow-origin', '*');
    request.response.headers.append(
      'access-control-allow-headers',
      'Origin, X-Requested-With, Content-Type, Accept, Range',
    );
    request.response.body = {
      info: 'OK',
    };
    return request.response;
  },
  method: 'GET',
  path: '/cors',
});

I think this can be added to the documentation.

Error when running example

Running the first example on deno results in an error.

import pogo from 'https://deno.land/x/pogo/main.ts';

const server = pogo.server({ port : 3000 });

server.router.get('/', () => {
    return 'Hello, world!';
});

server.start();

deno run --unstable --allow-read=\ --allow-write=\ --allow-net .\server.js
error: 'implements', 'interface', 'let', 'package', 'private', 'protected', 'public', 'static', or 'yield' cannot be used as an identifier in strict mode at https://cdn.pika.dev/-/[email protected]/dist=es2019,mode=exports/index.d.ts:1:7,Expected Comma, got Some(Word(StandardLonghandProperties)) at https://cdn.pika.dev/-/[email protected]/dist=es2019,mode=exports/index.d.ts:1:17

Demo program fails

~/Documents/deno >>> deno ./server.ts --allow-net 0.0.0.0:8000
Compile file:///home/hampton/Documents/deno/server.ts
Download https://deno.land/x/pogo/main.js
Download https://deno.land/x/pogo/dependencies.js
Download https://deno.land/x/pogo/lib/respond.js
^[Download https://deno.land/x/pogo/lib/toolkit.js
Download https://deno.land/x/[email protected]/http_status.ts
error: Uncaught Other: Import 'https://deno.land/x/[email protected]/http_status.ts' failed: 404 Not Found
► js/errors.ts:33:12
at DenoError (js/errors.ts:22:5)
at maybeError (js/errors.ts:33:12)
at maybeThrowError (js/errors.ts:39:15)
at sendSync (js/dispatch.ts:137:5)
at fetchModuleMetaData (js/compiler.ts:137:19)
at _resolveModule (js/compiler.ts:254:12)
at js/compiler.ts:370:37
at resolveModuleNames (js/compiler.ts:368:24)
at resolveModuleNamesWorker (third_party/node_modules/typescript/lib/typescript.js:89230:127)
at resolveModuleNamesReusingOldState (third_party/node_modules/typescript/lib/typescript.js:89473:24)

First example doesnt work

Hello,

I am building my first api and I have randomly chosen pogo.

But first example doesnt work:

import pogo from 'https://deno.land/x/pogo/main.ts';

const server = pogo.server({ port : 3000 });

server.router.get('/', () => {
    return 'Hello, world!';
});

server.start();
C:\Users\lumir\Documents\GitHub\DENO_API>deno run --allow-net server.js
Check file:///C:/Users/lumir/Documents/GitHub/DENO_API/server.js
error: TS1205 [ERROR]: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'.
    NamedStat
    ~~~~~~~~~
    at https://deno.land/x/[email protected]/lib/util/read-dir-stats.ts:24:5

TS1205 [ERROR]: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'.
export { FileHandlerOptions } from './helpers/file.ts';
         ~~~~~~~~~~~~~~~~~~
    at https://deno.land/x/[email protected]/lib/types.ts:41:10

TS1205 [ERROR]: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'.
export { DirectoryHandlerOptions } from './helpers/directory.tsx';
         ~~~~~~~~~~~~~~~~~~~~~~~
    at https://deno.land/x/[email protected]/lib/types.ts:42:10

Found 3 errors.

Support Deno 0.3.x

Hi there. Thanks for creating this framework. :)

I tried to run the example :

import pogo from 'https://deno.land/x/pogo/main.js';

const app = pogo.server({ port : 3000 });

app.route({
    method : 'GET',
    path   : '/hello',
    handler() {
        return 'Hello, world!';
    }
});

app.start();

But I ran into this error.

> $ deno server.ts                                                                                                                             [±master ●]
Compiling file:///Users/nilsnh/deno-testing/server.ts
Downloading https://deno.land/x/pogo/main.js
Downloading https://raw.githubusercontent.com/sholladay/pogo/master/dependencies.js
Downloading https://raw.githubusercontent.com/sholladay/pogo/master/lib/respond.js
Downloading https://raw.githubusercontent.com/sholladay/pogo/master/lib/toolkit.js
Downloading https://deno.land/x/http@61a3911be7e01273e13bf35a3a16285f413f0b70/http_status.ts
Downloading https://deno.land/x/http@61a3911be7e01273e13bf35a3a16285f413f0b70/mod.ts
Compiling https://raw.githubusercontent.com/denoland/deno_std/61a3911be7e01273e13bf35a3a16285f413f0b70/http/mod.ts
Downloading https://raw.githubusercontent.com/denoland/deno_std/61a3911be7e01273e13bf35a3a16285f413f0b70/http/http.ts
Downloading https://raw.githubusercontent.com/denoland/deno_std/61a3911be7e01273e13bf35a3a16285f413f0b70/http/deno
Uncaught Other: Import 'https://raw.githubusercontent.com/denoland/deno_std/61a3911be7e01273e13bf35a3a16285f413f0b70/http/deno' failed: 404 Not Found
    at DenoError (js/errors.ts:22:5)
    at maybeError (js/errors.ts:33:12)
    at maybeThrowError (js/errors.ts:39:15)
    at sendSync (js/dispatch.ts:86:5)
    at fetchModuleMetaData (js/os.ts:73:19)
    at _resolveModule (js/compiler.ts:255:38)
    at resolveModuleNames (js/compiler.ts:486:35)
    at compilerHost.resolveModuleNames (third_party/node_modules/typescript/lib/typescript.js:121106:138)
    at resolveModuleNamesWorker (third_party/node_modules/typescript/lib/typescript.js:88311:127)
    at resolveModuleNamesReusingOldState (third_party/node_modules/typescript/lib/typescript.js:88553:24)

My versions. I'm on MacOS Mojave 10.14.4.

> $ deno --version                                                                                                                                  [±master ●]
deno: 0.3.7
v8: 7.4.238
typescript: 3.4.1

URL constructor in Deno 1.2

In Deno 1.2, this simple code fails with Type 'URL' is not assignable to type 'string'.

import React from 'https://dev.jspm.io/react';
import pogo from 'https://deno.land/x/pogo/main.ts';

const server = pogo.server({ port : 3000 });

server.router.get('/', () => {
    return <h1>Hello, world!</h1>;
});

server.start();

Definition of URL constructor has changed in 1.2 by PR and I think you need to upgrade your package. Thx

not able to run example : server.js

I have a question: I am trying to run the example server.js which is throwing me these errors =

error: TS1205 [ERROR]: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'.
NamedStat
~~~~~~~~~
at https://deno.land/x/[email protected]/lib/util/read-dir-stats.ts:24:5

TS1205 [ERROR]: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'.
export { FileHandlerOptions } from './helpers/file.ts';
~~~~~~~~~~~~~~~~~~
at https://deno.land/x/[email protected]/lib/types.ts:41:10

TS1205 [ERROR]: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'.
export { DirectoryHandlerOptions } from './helpers/directory.tsx';
~~~~~~~~~~~~~~~~~~~~~~~
at https://deno.land/x/[email protected]/lib/types.ts:42:10

Found 3 errors.

TypeScript conversion

sholladay:
The TS conversion is almost done. I can give this a look soon after it's merged. If you could help me test the typescript branch, that would help move things along.

React and JSX support

import React from 'https://dev.jspm.io/react';
// ...
server.router.get('/', () => {
    return <p>Hello, world!</p>;
});

gives me the following error

error TS2304: Cannot find name 'p'.

► file:///Users/sock/code/denoAccubrew/server.ts:16:13

16     return <p>Hello, world!</p>;
               ^

error TS2304: Cannot find name 'Hello'.

► file:///Users/sock/code/denoAccubrew/server.ts:16:15

16     return <p>Hello, world!</p>;
                 ~~~~~

error TS2304: Cannot find name 'world'.

► file:///Users/sock/code/denoAccubrew/server.ts:16:22

16     return <p>Hello, world!</p>;
                        ~~~~~

error TS1161: Unterminated regular expression literal.

► 

Found 4 errors.

Support for React@17

There seem to be type conflicts (I believe) between pogo and React 17.

Here is the import map for all dependencies. (react-dom is in there twice for react-dom/server)

{
  "imports": {
    "pogo": "https://deno.land/x/[email protected]/main.ts",
    "react": "https://cdn.skypack.dev/react@^17?dts",
    "react-dom": "https://cdn.skypack.dev/react-dom@^17?dts",
    "react-dom/": "https://dev.jspm.io/[email protected]/"
  }
}

Here is a bit of errors from the deno type checking

TS2717 [ERROR]: Subsequent property declarations must have the same type.  Property 'tspan' must be of type 'SVGProps<SVGTSpanElement>', but here has type 'SVGProps<SVGTSpanElement>'.
            tspan: React.SVGProps<SVGTSpanElement>;
            ~~~~~
    at https://cdn.skypack.dev/-/[email protected]/dist=es2020,mode=types/index.d.ts:3170:13

    'tspan' was also declared here.
          tspan: React.SVGProps<SVGTSpanElement>;
          ~~~~~
        at https://raw.githubusercontent.com/soremwar/deno_types/b5a146610e2c97c1612371fcf610b541f950ee73/react/v16.13.1/react.d.ts:3822:7

TS2717 [ERROR]: Subsequent property declarations must have the same type.  Property 'use' must be of type 'SVGProps<SVGUseElement>', but here has type 'SVGProps<SVGUseElement>'.
            use: React.SVGProps<SVGUseElement>;
            ~~~
    at https://cdn.skypack.dev/-/[email protected]/dist=es2020,mode=types/index.d.ts:3171:13

    'use' was also declared here.
          use: React.SVGProps<SVGUseElement>;
          ~~~
        at https://raw.githubusercontent.com/soremwar/deno_types/b5a146610e2c97c1612371fcf610b541f950ee73/react/v16.13.1/react.d.ts:3823:7

TS2717 [ERROR]: Subsequent property declarations must have the same type.  Property 'view' must be of type 'SVGProps<SVGViewElement>', but here has type 'SVGProps<SVGViewElement>'.
            view: React.SVGProps<SVGViewElement>;
            ~~~~
    at https://cdn.skypack.dev/-/[email protected]/dist=es2020,mode=types/index.d.ts:3172:13

    'view' was also declared here.
          view: React.SVGProps<SVGViewElement>;
          ~~~~
        at https://raw.githubusercontent.com/soremwar/deno_types/b5a146610e2c97c1612371fcf610b541f950ee73/react/v16.13.1/react.d.ts:3824:7

Found 180 errors.

Feel free to ask if you have any other questions! :)

Can't send file over IP

Hi,
Can someone please confirm that pogo works with the latest version of deno?
I've been trying out deno's built in http, oak and abc and haven't had problems, but pogo won't even work over ipv4, only localhost for some reason.
On localhost return 'Hello, world!'; works correctly, but return h.file('index.html'); gives {"error":"Internal Server Error","message":"Internal Server Error","status":500} on the browser.
There are no errors on the server console.

Setting status code isn't working

Thank you so much for creating pogo! The implementation + documentation is inspiring, I'm happy here!

I'm trying to send a 401 status code and it's not working. I'm getting a 200 if I only try .status or .code or both. May I have some guidance on what I'm doing wrong please?

Thank you!

import pogo from 'https://deno.land/x/pogo/main.js'
import { Status as status } from 'https://deno.land/std/http/http_status.ts';

let app = pogo.server({ port : 3000 })

app.router.get('/', (req) => {
  req.response.status = 401
  req.response.code(status[401])
  console.log(req.response.status) // logs Unauthorized but Insomnia + browser say 200

  return 'Hello world!'
})

app.start()

Allow React support to be optional

It would be nice if React support was optional, such that if you didn't want to use it, the dependencies aren't downloaded. Currently, the entire React library is imported whether or not you want to use it, which might not be ideal for something like a pure API.

It could be done by allowing React to be added in manually by importing something like react.ts (which would be next to main.ts) and using its exports, or by allowing React to be imported by the consumer and then provided to the API (this would allow users to select a specific version of React as well, or maybe even be able to use Preact).

I'm not sure on the best way to implement it, but if this is something worth looking into, I'd be happy to try and write up a PR!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.