现场演示:链接
跳到新功能?
Node.js Web应用程序的样板。
如果您过去曾参加过任何黑客马拉松,那么您就会知道开始一个项目需要多少时间开始:确定要构建的内容,选择编程语言,选择网络框架,选择CSS框架。不久之后,您可能会在Github上进行初始项目,只有这样,其他团队成员才能开始贡献。还是做像通过Facebook身份验证签名这样的简单的事情?如果您不熟悉Oauth 2.0的工作原理,则可以花几个小时。
当我启动这个项目时,我的主要重点是简单性和易用性。我还试图使其尽可能通用和重复使用,以涵盖Hackathon Web应用程序的大多数用例,而不会太具体。在最坏的情况下,您可以将其用作项目的学习指南,例如,如果您只想登录Google Authentication,而无需登录。
“很好!独自一人已经是金了!”
- 阿德里安·勒·巴斯(Adrian Le Bas)
“太棒了。简直太棒了。”
- 史蒂文·鲁特(Steven Rueter)
“我已经使用了一年,而且许多项目,这是一个很棒的样板,并且该项目维护得很好!”
- 凯文·格兰杰(Kevin Granger)
“与萨哈特(Sahat)的项目相关的小世界。上周末,我们正在为黑客马拉松(Hackathon)使用他的黑客马拉松首发球员,并获得了一些奖品。真的很方便!”
- 面试我曾经与之合作的公司之一的候选人。
MongoDB(本地安装或托管)
Node.js 18+
命令行工具
Mac OS X: Xcode(或OS X 10.9+ : xcode-select --install )
Windows: Visual Studio Code + Windows子系统用于Linux -Ubuntu或Visual Studio
ubuntu / linux薄荷: sudo apt-get install build-essential
Fedora : sudo dnf groupinstall "Development Tools"
opensuse: sudo zypper install --type pattern devel_basis
注意:如果您是Node或Express的新手,则可以找到Node.js&Express From Scratch系列,有助于学习Node和Express的基础。另外,这是一个完整初学者的另一个很棒的教程 - 启动Node.js,Express,MongoDB。
步骤1:最简单的开始方法是克隆存储库:
# Get the latest snapshot
git clone https://github.com/sahat/hackathon-starter.git myproject
# Change directory
cd myproject
# Install NPM dependencies
npm install
# Then simply start your app
node app.js注意:我强烈建议安装Nodemon。它查看您的node.js应用程序中的任何更改,并自动重新启动服务器。安装后,而不是node app.js使用nodemon app.js从长远来看,它将为您节省很多时间,因为每次更改代码时,您都不需要手动重新启动服务器。要安装,请运行sudo npm install -g nodemon 。
步骤2:在完成步骤1和本地安装MongoDB后需要获取API键并更改配置,您应该能够通过Web浏览器访问应用程序并使用本地用户帐户。但是,在您从服务提供商那里获取特定密钥之前,某些功能诸如API集成之类的功能可能无法正常运行。项目中提供的钥匙是占位符,您可以保留它们的功能,以获取当前使用的功能。为了将获得的密钥纳入应用程序,您有两个选择:
export命令: export FACEBOOK_SECRET=xxxxxx 。这种方法被认为是一种更好的做法,因为它降低了在代码存储库中意外将您的秘密包含在内的风险。.env.example文件中替换键:打开.env.example文件,然后用新获取的键更新占位符键。此方法有意外检查您对代码存储库的秘密的风险。获得和配置的内容:
SMTP
recaptcha
OAuth进行社交登录(登录 /登录)
如果您打算使用API示例,则在API示例中使用的API键。
Mongodb地图集
电子邮件
NGrok和HTTPS如果您想使用一些需要HTTP的API(例如Pinterest或Facebook),则需要下载Ngrok。启动NGrok,将您的base_url设置为转发地址(即https://3ccb-1234-abcd.ngrok-free.app ),然后使用转发地址访问您的应用程序。如果您使用的是Ngrok之类的代理,则如果您尝试在http://localhost:8080而不是https://...ngrok-free.app地址访问该应用程序,则可能会遇到CSRF不匹配错误。
安装或下载独立的Ngrok客户端后,您可以启动NGrok,以拦截端口8080上的数据,并在Linux或Ngrok HTTP 8080或ngrok http 8080中使用./ngrok http 8080 。
步骤3:开发您的应用程序并自定义体验
步骤4:可选 - 部署到生产,请参阅:
您需要为API和服务提供所需的API和服务提供适当的凭据(客户ID,客户端秘密,API键或用户名和密码)。有关更多信息,请参见“入门”部分中的步骤2。
从提供商那里获取用于交易电子邮件的SMTP凭据。相应地设置SMTP_USER,SMTP_Password和SMTP_HOST环境变量。选择SMTP主机时,请记住该应用程序被配置为在端口465上使用安全的SMTP传输。您可以灵活地选择适合您需求或利用以下提供商之一的提供商,每个提供商都提供免费的层,以方便您。
| 提供者 | 免费层 | 网站 |
|---|---|---|
| sendgrid | 免费的100封电子邮件 | https://sendgrid.com |
| SMTP2GO | 免费每月1000封电子邮件 | https://www.smtp2go.com |
| Brevo | 免费300封电子邮件/天免费 | https://www.brevo.com |
.env中。这些密钥将在设置下可访问,如果您稍后再需要,请retaptcha键下降http://localhost:8080等)http://localhost:8080/auth/google/callback ).env中http://localhost:8080/auth/snapchat/callback ).env中.env中.env中.env中的Facebook_secretlocalhosthttp://localhost:8080等)http://localhost:8080/auth/facebook/callback )注意:成功与Facebook进行了成功登录后,将用户将用户重定向到主页,并在URL中使用Hash #_=_ 。这不是错误。有关处理方法的方法,请参阅此堆栈溢出讨论。
http://localhost:8080等)作为主页URL。http://localhost:8080/auth/github/callback ).env文件中http://localhost:8080等)。http://localhost:8080/auth/twitter/callback ).env文件中http://localhost:8080/auth/linkedin/callback )http://localhost:8080等)。r_basicprofile.env文件中.env文件中.env文件中http://localhost:8080/auth/foursquare/callback ).env文件中http://localhost:8080/auth/tumblr/callback ).env文件.env文件中http://localhost:8080/auth/twitch/callback ).env.env中.env文件中。.env文件的客户ID和客户端秘密| 姓名 | 描述 |
|---|---|
| config /passport.js | 护照本地和OAUTH策略,以及登录中间件。 |
| 控制器/Api.js | /API路由和所有API示例的控制器。 |
| 控制器/contact.js | 触点表的控制器。 |
| 控制器/home.js | 主页的控制器(索引)。 |
| 控制器/user.js | 用户帐户管理的控制器。 |
| 型号/user.js | 用户的杂种模式和模型。 |
| 民众/ | 静态资产(字体,CSS,JS,IMG)。 |
| public / js / application.js | 指定客户端JavaScript依赖项。 |
| public / js / app.js | 将客户端的JavaScript放在此处。 |
| public / css / main.scss | 您的应用程序的主要样式表。 |
| 视图 /帐户/ | 登录模板,密码重置,注册,配置文件。 |
| 视图 /API / | API示例的模板。 |
| 视图 /partials /flash.pug | 错误,信息和成功闪存通知。 |
| 视图 /partials /header.pug | NAVBAR部分模板。 |
| 视图 /partials /footer.pug | 页脚部分模板。 |
| views /layout.pug | 基本模板。 |
| views /home.pug | 主页模板。 |
| .dockerignore | 文件夹和文件被Docker使用忽略。 |
| .env.example | 您的API键,令牌,密码和数据库URI。 |
| .eslintrc | Eslint Linter的规则。 |
| .gitignore | 文件夹和git忽略的文件。 |
| app.js | 主申请文件。 |
| docker-compose.yml | Docker组成配置文件。 |
| Dockerfile | Docker配置文件。 |
| package.json | NPM依赖性。 |
| 包裹锁 | 包含package.json中的NPM依赖项的精确版本。 |
注意:您对您的姓名或构建视图的方式不偏爱。如果这使您更容易,则可以将所有模板放在顶级views目录中而无需嵌套文件夹结构。只是不要忘记更新extends ../layout和Controller中的相应res.render()路径。
| 包裹 | 描述 |
|---|---|
| @fortawesome/fontawesome | 符号和图标库。 |
| @googleapis/drive | Google Drive API集成库。 |
| @googleapis/sheet | Google表API集成库。 |
| @ladjs/bootstrap-social | 社交按钮库。 |
| @lob/lob-typescript-sdk | LOB(USPS邮件 /物理邮件服务)库。 |
| @node-rs/bcrypt | 用于哈希和腌制用户密码的库。 |
| @octokit/休息 | GitHub API库。 |
| @Passport-JS/Passport-Twitter | X(Twitter)登录支持(OAUTH 2)。 |
| @popperjs/core | 前端JS库,用于Poppers和Tooltips。 |
| 轴 | HTTP客户端。 |
| 身体偏远者 | Node.js身体解析中间件。 |
| 引导程序 | CSS框架。 |
| 柴 | BDD/TDD断言库。 |
| Cheerio | 使用jQuery风格的语法刮擦网页。 |
| 压缩 | Node.js压缩中间件。 |
| 连接蒙哥 | Express的MongoDB会话商店。 |
| Dotenv | 从.env文件加载环境变量。 |
| ErrorHandler | 仅开发错误处理程序中间件。 |
| eslint | Linter JavaScript。 |
| eslint-config-airbnb bas | Airbnb配置ESLINT。 |
| Eslint-Plugin-chai友好 | 使Eslint对Chai.js的“期望”和“应该”的陈述友好。 |
| Eslint-Plugin-Import | ESLINT插件具有有助于验证正确导入的规则。 |
| 表达 | Node.js Web框架。 |
| Express-Flash | 为Express提供闪存消息。 |
| 快速速率限制 | 限制滥用保护的中间件。 |
| 明确 | 简单的会话中间件。 |
| 沙哑 | Git Hook Manager可以使用GIT自动化任务。 |
| jQuery | 前端JS库与HTML元素交互。 |
| LastFM | last.fm API库。 |
| 绒毛阶段 | git上演的棉绒文件的实用程序。 |
| 高球 | LOB API库。 |
| Lodash | 用于使用数组,数字,对象,字符串的实用程序库。 |
| 卢斯卡 | CSRF中间件。 |
| MailChecker | 验证电子邮件地址有效而不是一次性地址。 |
| 摩卡 | 测试框架。 |
| 片刻 | 解析,验证,计算日期和时间。 |
| Mongodbmemoryserver | MongoDB在内存中(用于未运行DB的运行测试)。 |
| 猫鼬 | MongoDB ODM。 |
| 摩根 | http请求logger中间件。 |
| Multer | Node.js中间件,用于处理multipart/form-data 。 |
| Nodemailer | node.js库发送电子邮件。 |
| 纽约 | 覆盖测试。 |
| 护照 | Node.js的简单优雅的身份验证库 |
| 护照 - 脸书 | 使用Facebook插件登录。 |
| Passport-GitHub2 | 使用GitHub插件登录。 |
| Passport-Google-Oauth | 使用Google插件登录。 |
| 护照 - 链接蛋白 - 欧洲2号 | 使用LinkedIn插件登录。 |
| 护照本地 | 使用用户名和密码插件登录。 |
| 护照欧洲 | 允许您设置自己的OAuth 1.0A和OAuth 2.0策略。 |
| Passport-Oauth2-Refresh | 使用刷新令牌刷新OAuth 2.0访问令牌的库。 |
| 护照扣 | 使用Snapchat插件登录。 |
| 护照式 - 开机 | OpenID 2.0蒸汽插件。 |
| 补丁包 | 修复维护者修复前方的损坏节点模块。 |
| PayPal-Rest-SDK | PayPal API库。 |
| 帕格 | Express的模板引擎。 |
| Sass | SASS编译器以产生超级大国的CSS |
| 辛农 | 测试间谍,存根和模拟JavaScript。 |
| 条纹 | 官方条纹API库。 |
| 超级 | HTTP断言库。 |
| Twilio | Twilio API库。 |
| Twitch-Passport | 用Twitch插件登录。 |
| 验证器 | 字符串验证器和消毒器的库。 |
filesize(265318); // "265.32 kB" 。var token = _.find(req.user.tokens, { kind: 'twitter' }); ,其中第一个参数是一个数组,第二个参数是要搜索的对象。 403 Error: Forbidden ?您需要将以下隐藏的输入元素添加到表单中。作为CSRF保护的一部分,已将其添加在拉动请求#40中。
input(type='hidden', name='_csrf', value=_csrf)
注意:现在可以使用某些URL。换句话说,您可以指定应绕过CSRF验证检查的路由列表。
注2:要使用CSRF中间件内的正则表达式测试,请查看req.originalUrl是否匹配您所需的模式。
这是app.js中定义的自定义错误消息,以指示连接到mongodb的问题:
mongoose . connection . on ( 'error' , ( err ) => {
console . error ( err ) ;
console . log ( '%s MongoDB connection error. Please make sure MongoDB is running.' , chalk . red ( '✗' ) ) ;
process . exit ( ) ;
} ) ;在启动app.js之前,您需要让MongoDB服务器运行。您可以在此处下载MongoDB,也可以通过软件包管理器安装它。 Windows用户,阅读Windows上的MongoDB。
提示:如果您始终连接到Internet,则可以使用MongoDB Atlas,而不是在本地下载和安装MongoDB。您只需要在.env文件中更新数据库凭据即可。
您没有更改.env中的数据库URI 。如果将MONGODB设置为localhost ,则只要MongoDB运行,它将仅在您的计算机上使用。当您部署渲染,OpenShift或其他一些提供商时,您将不会在localhost上运行MongoDB。您需要使用MongoDB地图集创建一个帐户,然后创建一个免费的层数据库。有关如何与MongoDB Atlas逐步设置帐户以及新的数据库的更多信息,请参见部署。
为了简单起见。虽然可能有更好的方法,例如此博客中概述的每个控制器将app程序上下文传递给每个控制器,但我发现这种风格会使初学者感到困惑。我花了很长时间才能掌握exports和module.exports的概念。Exports,更不用说在其他文件中具有全局app参考。对我来说,这是落后的想法。 app.js是“应用程序的心脏”,它应该是一个引用模型,路线,控制器等。当在小型项目上独奏时,我更喜欢在app.js中使用所有内容,就像此REST AST API服务器一样。
本节旨在为您提供有关特定功能如何工作的详细说明。也许您只是对它的工作原理感到好奇,或者也许在阅读代码时会迷失和困惑,我希望它为您提供一些指导。
HTML5 UP有许多漂亮的模板,您可以免费下载。
下载zip文件时,它将随附index.html ,图像, CSS和JS文件夹。那么,您如何将其与黑客马拉松首发者集成在一起? Hackathon Starter使用Bootstrap CSS框架,但这些模板不使用。尝试同时使用两个CSS文件可能会导致不希望的效果。
注意:使用自定义模板方法,您应该了解您无法重复使用我创建的任何视图:布局,主页,API浏览器,登录,注册,注册,帐户管理,联系人。这些视图是使用Bootstrap网格和样式建立的。您将必须使用模板中提供的不同语法手动更新网格。话虽如此,如果您愿意,您可以混合和匹配:将Bootstrap用于主应用接口,以及用于登录页面的自定义模板。
让我们从头开始。对于此示例,我将使用逃生速度模板:
注意:为了简单起见,我将仅考虑index.html ,然后跳过left-sidebar.html , no-sidebar.html , right-sidebar.html 。
将所有JavaScript文件从html5up-escape-velocity/js移动到public/js 。然后将所有CSS文件从html5up-escape-velocity/css移动到public/css 。最后,将所有图像从html5up-escape-velocity/images移动到public/images 。您可以将其移至现有的IMG文件夹,但这需要手动更改每个img参考。抓住index.html的内容。
注意:不要忘记相应地更新所有CSS和JS路径。
创建一个新的文件escape-velocity.pug views每当您看到代码res.render('account/login')时,这意味着它将搜索views/account/login.pug文件。
让我们看看它的外观。在controllers/home.js中创建一个新的控制器逃生级别:
exports . escapeVelocity = ( req , res ) => {
res . render ( 'escape-velocity' , {
title : 'Landing Page'
} ) ;
} ;然后在app.js中创建路由。我将其放在索引控制器之后:
app . get ( '/escape-velocity' , homeController . escapeVelocity ) ;重新启动服务器(如果您不使用Nodemon );然后,您应该在http://localhost:8080/escape-velocity上查看新模板
我将在这里停下来,但是如果您想将此模板用作单页,请看一下这些哈巴狗模板的工作方式: layout.pug基本模板, index.pug papug-主页, partials/header.pug partials/footer.pug /header。您将必须手动将其分解成较小的碎片。弄清您要在所有页面上保持相同的模板的哪个部分 - 这是您的新layout.pug 。然后,每个更改layout.pug页面block content无论是index.pug , about.pug contact.pug使用现有模板作为参考。
这是一个相当漫长的过程,您从其他地方获得的模板可能具有另一个网格系统。这就是为什么我选择Bootstrap作为黑客马拉松首发球员的原因。许多人已经熟悉Bootstrap ,而且如果您从未使用过Bootstrap ,则很容易开始。您还可以在主题林上购买许多设计精美的引导主题,并将其用作Hackathon Starter的替换。但是,如果您想使用完全自定义的HTML/CSS设计,这应该有助于您开始!
Flash消息允许您在请求末尾显示消息,并在下一个请求中访问它,仅在下一个请求中访问。例如,在失败的登录尝试中,您将显示带有一些错误消息的警报,但是一旦刷新该页面或访问其他页面并返回登录页面,该错误消息就会消失。它仅显示一次。该项目使用快速消息的Express-Flash模块。该模块建立在Connect-Flash之上,这是我最初在该项目中使用的。使用Express-Flash,您不必向res.render()中的每个视图明确发送闪存消息。由于Express-Flash ,默认情况下,您的messages中的所有闪存消息都可以在您的视图中使用。
Flash消息具有两个步骤的过程。您使用req.flash('errors', { msg: 'Error messages goes here' }在控制器中创建闪存消息,然后在您的视图中显示它们:
if messages . errors
.alert.alert-danger.fade.in
for error in messages . errors
div = error . msg在第一步中, 'errors'是Flash消息的名称,该名称应与视图中messages对象上的属性名称匹配。 if message.errors您不想显示闪存消息,则在内部将警报消息放在内部。为了{ msg: 'Error message goes here' }而不仅仅是字符串 - 'Error message goes here'原因是为了一致性。为了澄清一下,用于验证和消毒用户输入的Express-Validator模块将所有错误返回作为对象数组的所有错误,其中每个对象都有一个msg属性,并带有消息,为什么发生了错误。这是一个更笼统的示例,说明存在错误时返回的内容:
[
{ param : "name" , msg : "Name is required" , value : "<received input>" } ,
{ param : "email" , msg : "A valid email is required" , value : "<received input>" }
]为了保持与该样式一致,您应该将所有闪存消息传递为{ msg: 'My flash message' }而不是字符串。否则,您将看到一个没有错误消息的警报框。那是因为在局部/flash.pug模板中,它将尝试输出error.msg (即"My flash message".msg ),换句话说,它将尝试在字符串对象上调用一个msg方法,该方法将返回未定义。我刚才提到的有关错误的所有内容,也适用于“信息”和“成功”闪存消息,您甚至可以自己创建一个新的,例如:
数据使用控制器(示例)
req.flash('warning', { msg: 'You have exceeded 90% of your data usage' });
用户帐户页面(示例)
if messages . warning
.alert.alert-warning.fade.in
for warning in messages . warning
div = warning . msg partials/flash.pug是一个部分模板,包含闪存消息的格式。以前,闪存消息散布在每个使用闪存消息的视图中(联系,登录,注册,配置文件),但现在,它使用了干燥的方法。
flash消息部分模板包含在layout.pug ,以及页脚和导航。
body
include partials/header
.container
include partials/flash
block content
include partials/footer如果您对Flash消息还有其他疑问,请随时打开问题,我将相应地更新此迷你指南,或者如果您想包括我错过的内容,请发送拉动请求。
一种更正确的方式来说这将是“我如何创建新路线?”主文件app.js包含所有路由。每个路由都有与之关联的回调函数。有时,您会看到一条路线的三个或更多参数。在这样的情况下,第一个参数仍然是URL字符串,而中间参数则称为中间件。将中间件视为一扇门。 If this door prevents you from continuing forward, you won't get to your callback function. One such example is a route that requires authentication.
app . get ( '/account' , passportConfig . isAuthenticated , userController . getAccount ) ; It always goes from left to right. A user visits /account page. Then isAuthenticated middleware checks if you are authenticated:
exports . isAuthenticated = ( req , res , next ) => {
if ( req . isAuthenticated ( ) ) {
return next ( ) ;
}
res . redirect ( '/login' ) ;
} ; If you are authenticated, you let this visitor pass through your "door" by calling return next(); 。 It then proceeds to the next middleware until it reaches the last argument, which is a callback function that typically renders a template on GET requests or redirects on POST requests. In this case, if you are authenticated, you will be redirected to the Account Management page; otherwise, you will be redirected to the Login page.
exports . getAccount = ( req , res ) => {
res . render ( 'account/profile' , {
title : 'Account Management'
} ) ;
} ; Express.js has app.get , app.post , app.put , app.delete , but for the most part, you will only use the first two HTTP verbs, unless you are building a RESTful API. If you just want to display a page, then use GET , if you are submitting a form, sending a file then use POST .
Here is a typical workflow for adding new routes to your application. Let's say we are building a page that lists all books from the database.
Step 1. Start by defining a route.
app . get ( '/books' , bookController . getBooks ) ;Note: As of Express 4.x you can define your routes like so:
app . route ( '/books' )
. get ( bookController . getBooks )
. post ( bookController . createBooks )
. put ( bookController . updateBooks )
. delete ( bookController . deleteBooks )And here is how a route would look if it required an authentication and an authorization middleware:
app . route ( '/api/twitter' )
. all ( passportConfig . isAuthenticated )
. all ( passportConfig . isAuthorized )
. get ( apiController . getTwitter )
. post ( apiController . postTwitter ) Use whichever style makes sense to you. Either one is acceptable. I think that chaining HTTP verbs on app.route is a very clean and elegant approach, but on the other hand, I can no longer see all my routes at a glance when you have one route per line.
Step 2. Create a new schema and a model Book.js inside the models directory.
const mongoose = require ( 'mongoose' ) ;
const bookSchema = new mongoose . Schema ( {
name : String
} ) ;
const Book = mongoose . model ( 'Book' , bookSchema ) ;
module . exports = Book ; Step 3. Create a new controller file called book.js inside the controllers directory.
/**
* GET /books
* List all books.
*/
const Book = require ( '../models/Book.js' ) ;
exports . getBooks = ( req , res ) => {
Book . find ( ( err , docs ) => {
res . render ( 'books' , { books : docs } ) ;
} ) ;
} ; Step 4. Import that controller in app.js .
const bookController = require ( './controllers/book' ) ; Step 5. Create books.pug template.
extends layout
block content
.page-header
h3 All Books
ul
for book in books
li = book . name就是这样! I will say that you could have combined Step 1, 2, 3 as following:
app . get ( '/books' , ( req , res ) => {
Book . find ( ( err , docs ) => {
res . render ( 'books' , { books : docs } ) ;
} ) ;
} ) ; Sure, it's simpler, but as soon as you pass 1000 lines of code in app.js it becomes a little challenging to navigate the file. I mean, the whole point of this boilerplate project was to separate concerns, so you could work with your teammates without running into MERGE CONFLICTS . Imagine you have four developers working on a single app.js , I promise you it won't be fun resolving merge conflicts all the time. If you are the only developer, then it's okay. But as I said, once it gets up to a certain LoC size, it becomes difficult to maintain everything in a single file.
这就是其中的全部。 Express.js is super simple to use. Most of the time you will be dealing with other APIs to do the real work: Mongoose for querying database, socket.io for sending and receiving messages over WebSockets, sending emails via Nodemailer, form validation using validator.js library, parsing websites using Cheerio, etc.
Dan Stroot submitted an excellent pull request that adds a real-time dashboard with socket.io. And as much as I'd like to add it to the project, I think it violates one of the main principles of the Hackathon Starter:
When I started this project, my primary focus was on simplicity and ease of use. I also tried to make it as generic and reusable as possible to cover most use cases of hackathon web apps, without being too specific .
When I need to use socket.io, I really need it, but most of the time - I don't. But more importantly, WebSockets support is still experimental on most hosting providers. Due to past provider issues with WebSockets, I have not include socket.io as part of the Hackathon Starter. For now... If you need to use socket.io in your app, please continue reading.
First, you need to install socket.io:
npm install socket . io Replace const app = express(); with the following code:
const app = express ( ) ;
const server = require ( 'http' ) . Server ( app ) ;
const io = require ( 'socket.io' ) ( server ) ; I like to have the following code organization in app.js (from top to bottom): module dependencies, import controllers, import configs, connect to database, express configuration, routes, start the server, socket.io stuff. That way I always know where to look for things.
Add the following code at the end of app.js :
io . on ( 'connection' , ( socket ) => {
socket . emit ( 'greet' , { hello : 'Hey there browser!' } ) ;
socket . on ( 'respond' , ( data ) => {
console . log ( data ) ;
} ) ;
socket . on ( 'disconnect' , ( ) => {
console . log ( 'Socket disconnected' ) ;
} ) ;
} ) ;One last thing left to change:
app . listen ( app . get ( 'port' ) , ( ) => {到
server . listen ( app . get ( 'port' ) , ( ) => {At this point, we are done with the back-end.
You now have a choice - to include your JavaScript code in Pug templates or have all your client-side JavaScript in a separate file - in app.js . I admit, when I first started with Node.js and JavaScript in general, I placed all JavaScript code inside templates because I have access to template variables passed in from Express right then and there. It's the easiest thing you can do, but also the least efficient and harder to maintain. Since then I almost never include inline JavaScript inside templates anymore.
But it's also understandable if you want to take the easier road. Most of the time you don't even care about performance during hackathons, you just want to "get shit done" before the time runs out. Well, either way, use whichever approach makes more sense to you. At the end of the day, it's what you build that matters, not how you build it.
If you want to stick all your JavaScript inside templates, then in layout.pug - your main template file, add this to head block.
script ( src = '/socket.io/socket.io.js' )
script .
let socket = io . connect ( window . location . href );
socket . on ( ' greet ' , function ( data ) {
console . log (data);
socket . emit ( ' respond ' , { message : ' Hey there, server! ' });
}); Note: Notice the path of the socket.io.js , you don't actually have to have socket.io.js file anywhere in your project; it will be generated automatically at runtime.
If you want to have JavaScript code separate from templates, move that inline script code into app.js , inside the $(document).ready() function:
$ ( document ) . ready ( function ( ) {
// Place JavaScript code here...
let socket = io . connect ( window . location . href ) ;
socket . on ( 'greet' , function ( data ) {
console . log ( data ) ;
socket . emit ( 'respond' , { message : 'Hey there, server!' } ) ;
} ) ;
} ) ;And we are done!
Declares a read-only named constant.
const name = 'yourName' ;Declares a block scope local variable.
let index = 0 ; Using the `${}` syntax, strings can embed expressions.
const name = 'Oggy' ;
const age = 3 ;
console . log ( `My cat is named ${ name } and is ${ age } years old.` ) ; To import functions, objects, or primitives exported from an external module. These are the most common types of importing.
const name = require ( 'module-name' ) ; const { foo , bar } = require ( 'module-name' ) ;To export functions, objects, or primitives from a given file or module.
module . exports = { myFunction } ; module . exports . name = 'yourName' ; module . exports = myFunctionOrClass ; The spread operator allows an expression to be expanded in places where multiple arguments (for function calls) or multiple elements (for array literals) are expected.
myFunction ( ... iterableObject ) ; < ChildComponent { ... this . props } /> A Promise is used in asynchronous computations to represent an operation that hasn't completed yet but is expected in the future.
var p = new Promise ( function ( resolve , reject ) { } ) ; The catch() method returns a Promise and deals with rejected cases only.
p . catch ( function ( reason ) { /* handle rejection */ } ) ; The then() method returns a Promise. It takes two arguments: callback for the success & failure cases.
p . then ( function ( value ) { /* handle fulfillment */ } , function ( reason ) { /* handle rejection */ } ) ; The Promise.all(iterable) method returns a promise that resolves when all of the promises in the iterable argument have resolved or rejects with the reason of the first passed promise that rejects.
Promise . all ( [ p1 , p2 , p3 ] ) . then ( function ( values ) { console . log ( values ) } ) ; Arrow function expression. Shorter syntax & lexically binds the this value. Arrow functions are anonymous.
singleParam => { statements } ( ) => { statements } ( param1 , param2 ) => expression const arr = [ 1 , 2 , 3 , 4 , 5 ] ;
const squares = arr . map ( x => x * x ) ; The class declaration creates a new class using prototype-based inheritance.
class Person {
constructor ( name , age , gender ) {
this . name = name ;
this . age = age ;
this . gender = gender ;
}
incrementAge ( ) {
this . age ++ ;
}
}? Credits : DuckDuckGo and @DrkSephy.
?回到顶部
Math . floor ( Date . now ( ) / 1000 ) ; moment().unix();
var now = new Date ( ) ;
now . setMinutes ( now . getMinutes ( ) + 30 ) ; moment().add(30, 'minutes');
// DD-MM-YYYY
var now = new Date ( ) ;
var DD = now . getDate ( ) ;
var MM = now . getMonth ( ) + 1 ;
var YYYY = now . getFullYear ( ) ;
if ( DD < 10 ) {
DD = '0' + DD ;
}
if ( MM < 10 ) {
MM = '0' + MM ;
}
console . log ( MM + '-' + DD + '-' + YYYY ) ; // 03-30-2016 console.log(moment(new Date(), 'MM-DD-YYYY'));
// hh:mm (12 hour time with am/pm)
var now = new Date ( ) ;
var hours = now . getHours ( ) ;
var minutes = now . getMinutes ( ) ;
var amPm = hours >= 12 ? 'pm' : 'am' ;
hours = hours % 12 ;
hours = hours ? hours : 12 ;
minutes = minutes < 10 ? '0' + minutes : minutes ;
console . log ( hours + ':' + minutes + ' ' + amPm ) ; // 1:43 am console.log(moment(new Date(), 'hh:mm A'));
var today = new Date ( ) ;
var nextWeek = new Date ( today . getTime ( ) + 7 * 24 * 60 * 60 * 1000 ) ; moment().add(7, 'days');
var today = new Date ( ) ;
var yesterday = date . setDate ( date . getDate ( ) - 1 ) ; moment().add(-1, 'days');
?回到顶部
User . find ( ( err , users ) => {
console . log ( users ) ;
} ) ; let userEmail = '[email protected]' ;
User . findOne ( { email : userEmail } , ( err , user ) => {
console . log ( user ) ;
} ) ; User
. find ( )
. sort ( { _id : - 1 } )
. limit ( 5 )
. exec ( ( err , users ) => {
console . log ( users ) ;
} ) ; Let's suppose that each user has a votes field and you would like to count the total number of votes in your database across all users. One very inefficient way would be to loop through each document and manually accumulate the count. Or you could use MongoDB Aggregation Framework instead:
User . aggregate ( { $group : { _id : null , total : { $sum : '$votes' } } } , ( err , votesCount ) => {
console . log ( votesCount . total ) ;
} ) ;?回到顶部
You will need to install docker and docker-compose on your system. If you are using WSL, you will need to install Docker Desktop on Windows and docker-compose on WSL.
Docker installation
Common problems setting up docker
After installing docker, start the application with the following commands :
# To build the project while supressing most of the build messages
docker-compose build web
# To build the project without supressing the build messages or using cached data
docker-compose build --no-cache --progress=plain web
# To start the application (or to restart after making changes to the source code)
docker-compose up web
To view the app, find your docker IP address + port 8080 ( this will typically be http://localhost:8080/ ). To use a port other than 8080, you would need to modify the port in app.js, Dockerfile, and docker-compose.yml.
Once you are ready to deploy your app, you will need to create an account with a cloud platform to host it. These are not the only choices, but they are my top picks. Additionally, you can create an account with MongoDB Atlas and then pick one of the providers below. Again, there are plenty of other choices, and you are not limited to just the ones listed below.
Render provides free nodejs hosting for repos on Github and Gitlab.
0.0.0.0/0 . Click SAVE to save the 0.0.0.0/0 whitelist..env.example with this URI string. Make sure to replace the with the db User password that you created under the Security tab.We are deploying your changes . You will need to wait for the deployment to finish before using the DB in your application.sudo gem install rhc ?rhc login and enter your OpenShift credentialsrhc app create MyApp nodejs-0.10git remote add openshift YOUR_GIT_REMOTE Add these two lines to app.js , just place them anywhere before app.listen() :
var IP_ADDRESS = process . env . OPENSHIFT_NODEJS_IP || '127.0.0.1' ;
var PORT = process . env . OPENSHIFT_NODEJS_PORT || 8080 ; Then change app.listen() to:
app . listen ( PORT , IP_ADDRESS , ( ) => {
console . log ( `Express server listening on port ${ PORT } in ${ app . settings . env } mode` ) ;
} ) ; Add this to package.json , after name and version . This is necessary because, by default, OpenShift looks for server.js file. And by specifying supervisor app.js it will automatically restart the server when node.js process crashes.
"main" : "app.js" ,
"scripts" : {
"start" : "supervisor app.js"
} ,git push -f openshift master-f (force) flag because OpenShift creates a dummy server with the welcome page when you create a new Node.js app. Passing -f flag will override everything with your Hackathon Starter project repository. Do not run git pull as it will create unnecessary merge conflicts.git remote add azure [Azure Git URL]git push azure masterNOTE At this point it appears that Bluemix's free tier to host NodeJS apps is limited to 30 days. If you are looking for a free tier service to host your app, Render might be a better choice at this point
Create a Bluemix Account
Sign up for Bluemix, or use an existing account.
Download and install the Cloud Foundry CLI to push your applications to Bluemix.
Create a manifest.yml file in the root of your application.
applications:
- name: <your-app-name>
host: <your-app-host>
memory: 128M
services:
- myMongo-db-name
The host you use will determinate your application URL initially, eg <host>.mybluemix.net . The service name 'myMongo-db-name' is a declaration of your MongoDB service. If you are using other services like Watson for example, then you would declare them the same way.
$ cf login -a https://api.ng.bluemix.net
$ cf create-service mongodb 100 [your-service-name]
Note: this is a free and experiment verion of MongoDB instance. Use the MongoDB by Compose instance for production applications:
$ cf create-service compose-for-mongodb Standard [your-service-name]'
Push the application
$ cf push
$ cf env <your-app-name >
(To view the *environment variables* created for your application)
Done , now go to the staging domain ( <host>.mybluemix.net ) and see your app running.
Be sure to check out the full list of Watson services to forwarder enhance your application functionality with a little effort. Watson services are easy to get going; it is simply a RESTful API call. Here is an example of a Watson Toner Analyzer to understand the emotional context of a piece of text that you send to Watson.
Virtual Assistant - Deliver consistent and intelligent customer care across all channels and touchpoints with conversational AI.
Natural Language Understanding - Analyze text to extract meta-data from content such as concepts, entities, keywords and more.
Discovery - Accelerate business decisions and processes with an AI-powered intelligent document understanding and content analysis platform.
Orchestrate - Hand off tedious tasks to Watson and never work the same way again.
List of Watson Services.
Download and install Node.js
Select or create a Google Cloud Platform Console project
Enable billing for your project (there's a $300 free trial)
Install and initialize the Google Cloud SDK
Create an app.yaml file at the root of your hackathon-starter folder with the following contents:
runtime : nodejs
env : flex
manual_scaling :
instances : 1 Make sure you've set MONGODB_URI in .env.example
Run the following command to deploy the hackathon-starter app:
gcloud app deployMonitor your deployed app in the Cloud Console
View the logs for your app in the Cloud Console
If you are starting with this boilerplate to build an application for prod deployment, or if after your hackathon you would like to get your project hardened for production use, see prod-checklist.md.
You can find the changelog for the project in: CHANGELOG.md
If something is unclear, confusing, or needs to be refactored, please let me know. Pull requests are always welcome, but due to the opinionated nature of this project, I cannot accept every pull request. Please open an issue before submitting a pull request. This project uses Airbnb JavaScript Style Guide with a few minor exceptions. If you are submitting a pull request that involves Pug templates, please make sure you are using spaces , not tabs.
The MIT License (MIT)
Copyright (c) 2014-2023 Sahat Yalkabov
特此免费授予获得此软件副本和相关文档文件副本(“软件”)的任何人,以无限制处理该软件,包括无限制的使用权,复制,复制,修改,合并,合并,发布,分发,分发,分发,订婚,和/或允许软件的副本,并允许对以下条件提供以下条件,以下是以下条件。
上述版权通知和此许可通知应包含在软件的所有副本或大量部分中。
该软件是“原样”提供的,没有任何形式的明示或暗示保证,包括但不限于适销性,特定目的的适用性和非侵权的保证。在任何情况下,作者或版权持有人都不应对任何索赔,损害赔偿或其他责任责任,无论是在合同,侵权的诉讼中还是其他责任,是由软件,使用或与软件中的使用或其他交易有关的。