master cbc5ba79c372 cached
15 files
17.3 KB
5.1k tokens
11 symbols
1 requests
Download .txt
Repository: tomsoderlund/nextjs-express-mongoose-crudify-boilerplate
Branch: master
Commit: cbc5ba79c372
Files: 15
Total size: 17.3 KB

Directory structure:
gitextract_lwe4j0o4/

├── .gitignore
├── README.md
├── components/
│   ├── KittenItem.js
│   └── PageHead.js
├── config/
│   └── config.js
├── package.json
├── pages/
│   ├── _app.js
│   └── index.js
├── redux/
│   └── reduxApi.js
├── server/
│   ├── api/
│   │   └── kittens.js
│   ├── models/
│   │   └── kitten.js
│   ├── routes.js
│   ├── server.js
│   └── services/
│       └── helpers.js
└── static/
    └── app.css

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
node_modules/
.next/

================================================
FILE: README.md
================================================
# Next.js (React) + Redux + Express REST API + MongoDB + Mongoose-Crudify boilerplate

_Note: this is my v1 boilerplate for React web apps. See also my [Firebase and React Hooks boilerplate](https://github.com/tomsoderlund/nextjs-pwa-firebase-boilerplate), [GraphQL + Postgres SQL boilerplate](https://github.com/tomsoderlund/nextjs-pwa-graphql-sql-boilerplate), and [Redux + REST + Postgres SQL boilerplate](https://github.com/tomsoderlund/nextjs-sql-rest-api-boilerplate)._

This template is based on [nextjs-express-boilerplate](https://github.com/johhansantana/nextjs-express-boilerplate), but with added [mongoose-crudify](https://github.com/ryo718/mongoose-crudify) and [redux-api](https://github.com/lexich/redux-api).

## Support this project

Did you or your company find `nextjs-express-mongoose-crudify-boilerplate` useful? Please consider giving a small donation, it helps me spend more time on open-source projects:

[![Support Tom on Ko-Fi.com](https://www.tomsoderlund.com/ko-fi_tomsoderlund_50.png)](https://ko-fi.com/tomsoderlund)

## Why is this awesome?

This is a great starting point for a any project where you want **React + Redux** (with server-side rendering, powered by [Next.js](https://github.com/zeit/next.js)) as frontend and **Express/MongoDB** as a REST API backend.
_Lightning fast, all JavaScript._

* Simple REST API routes with MongoDB database and `mongoose-crudify`.
* Redux REST support with `redux-api` and `next-redux-wrapper`.
* Flexible client-side routing with `next-routes` (see `server/routes.js`).
* Flexible configuration with `config/config.js` and `.env` file.
* Hot reloading with `nodemon`.
* Testing with Jasmine.
* Code formatting and linting with StandardJS.
* JWT authentication for client-server communication (coming).

## Demo

See [**nextjs-express-mongoose-crudify-boilerplate** running on Heroku here](https://nextjs-express-mongoose.herokuapp.com/).

![nextjs-express-mongoose-crudify-boilerplate demo on Heroku](docs/kittens-demo.gif)

## Don’t want Redux?

This project now uses Redux and [redux-api](https://github.com/lexich/redux-api). See the [no-redux](https://github.com/tomsoderlund/nextjs-express-mongoose-crudify-boilerplate/tree/no-redux) branch for the (unmaintained) version without Redux.

## How to use

Clone this repository:

	git clone https://github.com/tomsoderlund/nextjs-express-mongoose-crudify-boilerplate.git [MY_APP]

Install dependencies:

	cd [MY_APP]
	yarn  # or npm install

Start it by doing the following:

	export MONGODB_URI=*your mongodb url* // you can get one for free at https://www.mlab.com/home
	yarn dev

In production:

	yarn build
	yarn start

If you navigate to `http://localhost:3001/` you will see a [Next.js](https://github.com/zeit/next.js) page with a list of kittens (or an empty list if you haven't added one).

You have your API server running at `http://localhost:3001/api/kittens`


## Deploying

### Deploying on Heroku

	heroku create [MY_APP]
	heroku addons:add mongolab
	git push heroku master

### Deploying on Now

See instructions on [nextjs-express-boilerplate](https://github.com/johhansantana/nextjs-express-boilerplate).


================================================
FILE: components/KittenItem.js
================================================
const KittenItem = ({ kitten, index, inProgress, handleUpdate, handleDelete }) => (
  <div className={inProgress === kitten._id ? 'inProgress' : ''}>
    {kitten.name}
    <a className='update' onClick={handleUpdate.bind(this, index, kitten._id)}>Update</a>
    <a className='delete' onClick={handleDelete.bind(this, index, kitten._id)}>Delete</a>
    <style jsx>{`
      a {
        margin-left: 0.5em;
        cursor: pointer;
        font-size: 0.6em;
        text-transform: uppercase;
      }
      a.update {
        color: lime;
      }
      a.delete {
        color: tomato;
      }
      .inProgress {
        opacity: 0.3;
      }
    `}</style>
  </div>
)
export default KittenItem


================================================
FILE: components/PageHead.js
================================================
import Head from 'next/head'

const PageHead = ({ title, description }) => (
  <Head>
    <title>{title}</title>
    <meta name='description' content={description} />
    <meta charSet='utf-8' />
    <meta httpEquiv='content-language' content='en' />
    <meta name='viewport' content='initial-scale=1.0, width=device-width' />
    <link rel='stylesheet' href='/static/app.css' />
  </Head>
)
export default PageHead


================================================
FILE: config/config.js
================================================
const appName = 'nextjs-express-mongoose-crudify-boilerplate'
const databaseName = 'nextjs-express-boilerplate'
const serverPort = process.env.PORT || 3122

const completeConfig = {

  default: {
    appName,
    serverPort,
    databaseUrl: process.env.MONGODB_URI || `mongodb://localhost/${databaseName}`,
    jsonOptions: {
      headers: {
        'Content-Type': 'application/json'
      }
    }
  },

  development: {
    appUrl: `http://localhost:${serverPort}/`
  },

  production: {
    appUrl: `https://nextjs-express-mongoose.herokuapp.com/`
  }

}

// Public API
module.exports = {
  config: { ...completeConfig.default, ...completeConfig[process.env.NODE_ENV] },
  completeConfig
}


================================================
FILE: package.json
================================================
{
	"name": "nextjs-express-mongoose-crudify-boilerplate",
	"version": "4.0.0",
	"description": "Next.js (React) + Redux + Express REST API + Mongoose CRUD boilerplate.",
	"main": "server/server.js",
	"license": "ISC",
	"scripts": {
		"test": "echo 'Running Standard.js and Jasmine unit tests...\n' && yarn lint && yarn unit",
		"unit": "jasmine",
		"lint": "standard",
		"fix": "standard --fix",
		"dev": "nodemon -w server -w package.json server/server.js",
		"build": "next build",
		"heroku-postbuild": "next build",
		"start": "NODE_ENV=production node server/server.js"
	},
	"now": {
		"name": "nextjs-express-mongoose-crudify-boilerplate",
		"alias": "nextjs-express-mongoose-crudify-boilerplate"
	},
	"engines": {
		"node": "^10.13.0",
		"yarn": "^1.3.2"
	},
	"dependencies": {
		"body-parser": "^1.15.2",
		"dotenv": "^6.2.0",
		"express": "^4.14.0",
		"glob": "^7.1.2",
		"isomorphic-fetch": "^2.2.1",
		"isomorphic-unfetch": "^2.0.0",
		"lodash": "^4.17.4",
		"mongoose": "^4.7.6",
		"mongoose-crudify": "^0.2.0",
		"next": "^8.0.3",
		"next-redux-wrapper": "^3.0.0-alpha.1",
		"next-routes": "^1.4.2",
		"react": "^16.0.0",
		"react-dom": "^16.0.0",
		"react-redux": "^5.0.6",
		"redux": "^3.7.2",
		"redux-api": "^0.11.1",
		"redux-thunk": "^2.2.0"
	},
	"devDependencies": {
		"babel-eslint": "^10.0.1",
		"jasmine": "^3.3.1",
		"nodemon": "^1.12.1",
		"standard": "^12.0.1"
	}
}


================================================
FILE: pages/_app.js
================================================
// pages/_app.js
import React from 'react'
import { Provider } from 'react-redux'
import App, { Container } from 'next/app'
import withRedux from 'next-redux-wrapper'
import { makeStore } from '../redux/reduxApi.js'

class MyApp extends App {
  static async getInitialProps ({ Component, ctx }) {
    return {
      pageProps: {
        // Call page-level getInitialProps
        ...(Component.getInitialProps ? await Component.getInitialProps(ctx) : {})
      }
    }
  }

  render () {
    const { Component, pageProps, store } = this.props
    return (
      <Container>
        <Provider store={store}>
          <Component {...pageProps} />
        </Provider>
      </Container>
    )
  }
}

export default withRedux(makeStore, { debug: false })(MyApp)


================================================
FILE: pages/index.js
================================================
import { Component } from 'react'

import reduxApi, { withKittens } from '../redux/reduxApi.js'

import { Link } from '../server/routes.js'
import PageHead from '../components/PageHead'
import KittenItem from '../components/KittenItem'

class IndexPage extends Component {
  static async getInitialProps ({ store, isServer, pathname, query }) {
    // Get all kittens
    const kittens = await store.dispatch(reduxApi.actions.kittens.sync())
    return { kittens, query }
  }

  constructor (props) {
    super(props)
    this.state = { name: '' }
  }

  handleChangeInputText (event) {
    this.setState({ name: event.target.value })
  }

  handleAdd (event) {
    const { name } = this.state
    if (!name) return
    const callbackWhenDone = () => this.setState({ name: '', inProgress: false })
    this.setState({ inProgress: true })
    // Actual data request
    const newKitten = { name }
    this.props.dispatch(reduxApi.actions.kittens.post({}, { body: JSON.stringify(newKitten) }, callbackWhenDone))
  }

  handleUpdate (kitten, index, kittenId, event) {
    const name = window.prompt('New name?', kitten.name)
    if (!name) return
    const callbackWhenDone = () => this.setState({ inProgress: false })
    this.setState({ inProgress: kittenId })
    // Actual data request
    const newKitten = { id: kittenId, name }
    this.props.dispatch(reduxApi.actions.kittens.put({ id: kittenId }, { body: JSON.stringify(newKitten) }, callbackWhenDone))
  }

  handleDelete (index, kittenId, event) {
    const callbackWhenDone = () => this.setState({ inProgress: false })
    this.setState({ inProgress: kittenId })
    // Actual data request
    this.props.dispatch(reduxApi.actions.kittens.delete({ id: kittenId }, callbackWhenDone))
  }

  render () {
    const { kittens } = this.props// dd

    const kittenList = kittens.data
      ? kittens.data.map((kitten, index) => <KittenItem
        key={index}
        kitten={kitten}
        index={index}
        inProgress={this.state.inProgress}
        handleUpdate={this.handleUpdate.bind(this, kitten)}
        handleDelete={this.handleDelete.bind(this)}
      />)
      : []

    return <main>
      <PageHead
        title='Next.js (React) + Express REST API + MongoDB + Mongoose-Crudify boilerplate'
        description='Demo of nextjs-express-mongoose-crudify-boilerplate'
      />

      <h1>Kittens</h1>

      {kittenList}
      <div>
        <input placeholder='Enter a kitten name' value={this.state.name} onChange={this.handleChangeInputText.bind(this)} disabled={this.state.inProgress} />
        <button onClick={this.handleAdd.bind(this)} disabled={this.state.inProgress}>Add kitten</button>
        <style jsx>{`
          div {
            margin-top: 1em;
          }
        `}</style>
      </div>

      <h2>Routing</h2>
      Current page slug: /{this.props.query.slug}
      <ul>
        <li><Link route='/about'><a>About</a></Link></li>
        <li><Link route='/more/contact'><a>Contact</a></Link></li>
      </ul>

    </main>
  };
}

export default withKittens(IndexPage)


================================================
FILE: redux/reduxApi.js
================================================
import _ from 'lodash'
import fetch from 'isomorphic-fetch'

import reduxApi, { transformers } from 'redux-api'
import adapterFetch from 'redux-api/lib/adapters/fetch'
import { createStore, applyMiddleware, combineReducers } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { connect } from 'react-redux'

const { config } = require('../config/config')

const apiTransformer = function (data, prevData, action) {
  const actionMethod = _.get(action, 'request.params.method')
  switch (actionMethod) {
    case 'POST':
      return [...prevData, data]
    case 'PUT':
      return prevData.map(oldData => oldData._id === data._id ? data : oldData)
    case 'DELETE':
      return _(prevData).filter(oldData => oldData._id === data._id ? undefined : oldData).compact().value()
    default:
      return transformers.array.call(this, data, prevData, action)
  }
}

// redux-api documentation: https://github.com/lexich/redux-api/blob/master/docs/DOCS.md
const thisReduxApi = reduxApi({

  // Simple endpoint description
  // oneKitten: '/api/kittens/:id',

  // Complex endpoint description
  kittens: {
    url: '/api/kittens/:id',
    crud: true, // Make CRUD actions: https://github.com/lexich/redux-api/blob/master/docs/DOCS.md#crud

    // base endpoint options `fetch(url, options)`
    options: config.jsonOptions,

    // reducer (state, action) {
    //  console.log('reducer', action);
    //  return state;
    // },

    // postfetch: [
    //  function ({data, actions, dispatch, getState, request}) {
    //    console.log('postfetch', {data, actions, dispatch, getState, request});
    //    dispatch(actions.kittens.sync());
    //  }
    // ],

    // Reimplement default `transformers.object`
    // transformer: transformers.array,
    transformer: apiTransformer

  }

})
  .use('fetch', adapterFetch(fetch))
  .use('rootUrl', config.appUrl)

export default thisReduxApi

const createStoreWithThunkMiddleware = applyMiddleware(thunkMiddleware)(createStore)
export const makeStore = (reduxState, enhancer) => createStoreWithThunkMiddleware(combineReducers(thisReduxApi.reducers), reduxState)

// endpointNames: Use reduxApi endpoint names here
const mapStateToProps = (endpointNames, reduxState) => {
  let props = {}
  for (let i in endpointNames) {
    props[endpointNames[i]] = reduxState[endpointNames[i]]
    props[`${endpointNames[i]}Actions`] = thisReduxApi.actions[endpointNames[i]]
  }
  return props
}

export const withReduxEndpoints = (PageComponent, endpointNames) => connect(mapStateToProps.bind(undefined, endpointNames))(PageComponent)
// Define custom endpoints/providers here:
export const withKittens = PageComponent => withReduxEndpoints(PageComponent, ['kittens'])


================================================
FILE: server/api/kittens.js
================================================
'use strict'

const mongooseCrudify = require('mongoose-crudify')

const helpers = require('../services/helpers')
const Kitten = require('../models/kitten')

module.exports = function (server) {
  // Docs: https://github.com/ryo718/mongoose-crudify
  server.use(
    '/api/kittens',
    mongooseCrudify({
      Model: Kitten,
      selectFields: '-__v', // Hide '__v' property
      endResponseInAction: false,

      // beforeActions: [],
      // actions: {}, // list (GET), create (POST), read (GET), update (PUT), delete (DELETE)
      afterActions: [
        { middlewares: [helpers.formatResponse] }
      ]
    })
  )
}


================================================
FILE: server/models/kitten.js
================================================
const mongoose = require('mongoose')

const Schema = mongoose.Schema

const kittenSchema = new Schema({
  name: { type: String, required: true }
})

module.exports = mongoose.model('Kitten', kittenSchema)


================================================
FILE: server/routes.js
================================================
const routes = require('next-routes')
const routesImplementation = routes()

// routesImplementation
//   .add([identifier], pattern = /identifier, page = identifier)
//   .add('/blog/:slug', 'blogShow')
//   .add('showBlogPostRoute', '/blog/:slug', 'blogShow')

routesImplementation.add('/:slug', 'index')
routesImplementation.add('/more/:slug', 'index')

module.exports = routesImplementation

// Usage inside Page.getInitialProps (req = { pathname, asPath, query } = { pathname: '/', asPath: '/about', query: { slug: 'about' } })


================================================
FILE: server/server.js
================================================
require('dotenv').config()

const express = require('express')
const server = express()
const bodyParser = require('body-parser')
const mongoose = require('mongoose')
const glob = require('glob')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })

const routes = require('./routes')
const routerHandler = routes.getRequestHandler(app)

const { config } = require('../config/config')

app.prepare().then(() => {
  // Parse application/x-www-form-urlencoded
  server.use(bodyParser.urlencoded({ extended: false }))
  // Parse application/json
  server.use(bodyParser.json())

  // Allows for cross origin domain request:
  server.use(function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*')
    res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
    next()
  })

  // MongoDB
  mongoose.Promise = Promise
  mongoose.connect(config.databaseUrl, { useMongoClient: true })
  const db = mongoose.connection
  db.on('error', console.error.bind(console, 'connection error:'))

  // REST API routes
  const rootPath = require('path').join(__dirname, '/..')
  glob.sync(rootPath + '/server/api/*.js').forEach(controllerPath => {
    if (!controllerPath.includes('.test.js')) require(controllerPath)(server)
  })

  // Next.js page routes
  server.get('*', routerHandler)

  // Start server
  server.listen(config.serverPort, () => console.log(`${config.appName} running on http://localhost:${config.serverPort}/`))
})


================================================
FILE: server/services/helpers.js
================================================
//
// Name:    helpers.js
// Purpose: Library for helper functions
// Creator: Tom Söderlund
//

'use strict'

const _ = require('lodash')

// Since DELETE doesn't return the _id of deleted item by default
module.exports.formatResponse = function (req, res, next) {
  if (req.crudify.err) console.error('formatResponse:', _.get(req, 'crudify.err.message'))
  return res.json(req.crudify.err || (req.method === 'DELETE' ? req.params : req.crudify.result))
}


================================================
FILE: static/app.css
================================================
* {
	box-sizing: border-box;
}

body {
	margin: 1em;
	font-family: sans-serif;
	font-size: 20px;
}

@media only screen and (max-width: 480px) {
	input, button {
		width: 100%;
	}
}

/* Nice & simple: Button - http://codepen.io/tomsoderlund/pen/qqyzqp */
button {
	background-color: dodgerblue;
	border-radius: 0.2em;
	border: none;
	box-shadow: 0 0.125em 0.125em rgba(0,0,0, 0.3);
	box-sizing: border-box;
	color: white;
	cursor: pointer;
	font-family: inherit;
	font-size: inherit;
	font-weight: bold;
	outline: none;
	padding: 0.6em;
	margin: 0.2em;
	transition: all 0.2s;
}
button:hover:not(:disabled) {
	opacity: 0.8;
	transition: box-shadow 0s;
}
button:active {
	margin-top: 0.3em;
	margin-bottom: 0.1em;
	box-shadow: 0 0.5px 0.125em rgba(0,0,0, 0.4);
}
button:disabled {
	background-color: silver;
}

/* Nice & simple: Input and Dropdown Menu - http://codepen.io/tomsoderlund/pen/GNBbWz */
input,
textarea,
select {
	outline: none;
	resize: none;
	box-shadow: inset 0 0.125em 0.125em rgba(0,0,0, 0.3);
	box-sizing: border-box;
	background-color: white;
	border-radius: 0.2em;
	border: 2px solid lightgray;
	color: inherit;
	font-family: inherit;
	font-size: inherit;
	padding: 0.6em;
	margin: 0.2em;
}
input:hover:not(:disabled),
textarea:hover:not(:disabled),
select:hover:not(:disabled) {
	border-color: silver;
}
input:focus,
textarea:focus,
select:focus {
	border-color: darkgray;
}
input:disabled,
textarea:disabled,
select:disabled {
	background-color: whitesmoke;
}
Download .txt
gitextract_lwe4j0o4/

├── .gitignore
├── README.md
├── components/
│   ├── KittenItem.js
│   └── PageHead.js
├── config/
│   └── config.js
├── package.json
├── pages/
│   ├── _app.js
│   └── index.js
├── redux/
│   └── reduxApi.js
├── server/
│   ├── api/
│   │   └── kittens.js
│   ├── models/
│   │   └── kitten.js
│   ├── routes.js
│   ├── server.js
│   └── services/
│       └── helpers.js
└── static/
    └── app.css
Download .txt
SYMBOL INDEX (11 symbols across 2 files)

FILE: pages/_app.js
  class MyApp (line 8) | class MyApp extends App {
    method getInitialProps (line 9) | static async getInitialProps ({ Component, ctx }) {
    method render (line 18) | render () {

FILE: pages/index.js
  class IndexPage (line 9) | class IndexPage extends Component {
    method getInitialProps (line 10) | static async getInitialProps ({ store, isServer, pathname, query }) {
    method constructor (line 16) | constructor (props) {
    method handleChangeInputText (line 21) | handleChangeInputText (event) {
    method handleAdd (line 25) | handleAdd (event) {
    method handleUpdate (line 35) | handleUpdate (kitten, index, kittenId, event) {
    method handleDelete (line 45) | handleDelete (index, kittenId, event) {
    method render (line 52) | render () {
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (19K chars).
[
  {
    "path": ".gitignore",
    "chars": 20,
    "preview": "node_modules/\n.next/"
  },
  {
    "path": "README.md",
    "chars": 3150,
    "preview": "# Next.js (React) + Redux + Express REST API + MongoDB + Mongoose-Crudify boilerplate\n\n_Note: this is my v1 boilerplate "
  },
  {
    "path": "components/KittenItem.js",
    "chars": 694,
    "preview": "const KittenItem = ({ kitten, index, inProgress, handleUpdate, handleDelete }) => (\n  <div className={inProgress === kit"
  },
  {
    "path": "components/PageHead.js",
    "chars": 417,
    "preview": "import Head from 'next/head'\n\nconst PageHead = ({ title, description }) => (\n  <Head>\n    <title>{title}</title>\n    <me"
  },
  {
    "path": "config/config.js",
    "chars": 695,
    "preview": "const appName = 'nextjs-express-mongoose-crudify-boilerplate'\nconst databaseName = 'nextjs-express-boilerplate'\nconst se"
  },
  {
    "path": "package.json",
    "chars": 1392,
    "preview": "{\n\t\"name\": \"nextjs-express-mongoose-crudify-boilerplate\",\n\t\"version\": \"4.0.0\",\n\t\"description\": \"Next.js (React) + Redux "
  },
  {
    "path": "pages/_app.js",
    "chars": 759,
    "preview": "// pages/_app.js\nimport React from 'react'\nimport { Provider } from 'react-redux'\nimport App, { Container } from 'next/a"
  },
  {
    "path": "pages/index.js",
    "chars": 3057,
    "preview": "import { Component } from 'react'\n\nimport reduxApi, { withKittens } from '../redux/reduxApi.js'\n\nimport { Link } from '."
  },
  {
    "path": "redux/reduxApi.js",
    "chars": 2715,
    "preview": "import _ from 'lodash'\nimport fetch from 'isomorphic-fetch'\n\nimport reduxApi, { transformers } from 'redux-api'\nimport a"
  },
  {
    "path": "server/api/kittens.js",
    "chars": 627,
    "preview": "'use strict'\n\nconst mongooseCrudify = require('mongoose-crudify')\n\nconst helpers = require('../services/helpers')\nconst "
  },
  {
    "path": "server/models/kitten.js",
    "chars": 205,
    "preview": "const mongoose = require('mongoose')\n\nconst Schema = mongoose.Schema\n\nconst kittenSchema = new Schema({\n  name: { type: "
  },
  {
    "path": "server/routes.js",
    "chars": 533,
    "preview": "const routes = require('next-routes')\nconst routesImplementation = routes()\n\n// routesImplementation\n//   .add([identifi"
  },
  {
    "path": "server/server.js",
    "chars": 1527,
    "preview": "require('dotenv').config()\n\nconst express = require('express')\nconst server = express()\nconst bodyParser = require('body"
  },
  {
    "path": "server/services/helpers.js",
    "chars": 457,
    "preview": "//\n// Name:    helpers.js\n// Purpose: Library for helper functions\n// Creator: Tom Söderlund\n//\n\n'use strict'\n\nconst _ ="
  },
  {
    "path": "static/app.css",
    "chars": 1479,
    "preview": "* {\n\tbox-sizing: border-box;\n}\n\nbody {\n\tmargin: 1em;\n\tfont-family: sans-serif;\n\tfont-size: 20px;\n}\n\n@media only screen a"
  }
]

About this extraction

This page contains the full source code of the tomsoderlund/nextjs-express-mongoose-crudify-boilerplate GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (17.3 KB), approximately 5.1k tokens, and a symbol index with 11 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!