Compare commits

..

221 Commits

Author SHA1 Message Date
Arik Chakma
69e8e5e19b Update how-to-setup-a-jump-server.md 2023-04-04 14:40:47 +06:00
Kamran Ahmed
9893e9f0a3 Add singlestore links 2023-04-03 22:33:03 +01:00
Kamran Ahmed
caf1cd0269 Minor update 2023-04-03 21:37:13 +01:00
Kamran Ahmed
d21c1f6d0d Add sponsors 2023-04-03 19:15:10 +01:00
Kamran Ahmed
9d38cf7650 Add liblab content 2023-04-03 15:26:15 +01:00
Kamran Ahmed
d232d3bbd0 Update sponsors 2023-04-01 01:37:24 +01:00
Dimension
366d893df9 Fix invalid link (#3734)
Co-authored-by: Hitesh <hitesh.g@smarter.codes>
2023-03-30 18:20:01 +01:00
Kamran Ahmed
ff27561765 Remove unused styles 2023-03-30 18:17:27 +01:00
Kamran Ahmed
133642e05f Add missing content for backend roadmap 2023-03-30 16:34:39 +01:00
Kamran Ahmed
7434ff71eb Run prettier 2023-03-30 01:23:01 +01:00
Arik Chakma
d081ecf5b3 tooling: prettier for astro and tailwind (#3732)
* tooling: prettier for astro and tailwind

Prettier configuration for Astro component's formatting and Tailwind CSS class sorting.

* fix: single quote for props
2023-03-30 01:10:47 +01:00
Kamran Ahmed
d8a039690b Add a button to join on discord 2023-03-28 17:53:48 +01:00
Rahul Saw
56f0df549d Removed a link (#3697)
I think there is no need to use one more link for the exact same example.
The link that I have removed, redirects the user to the old react documentation which is already listed in this md file as "Reusing Logic with Custom Hooks".
2023-03-28 17:07:43 +01:00
Kamran Ahmed
b042161e29 Broken style of code 2023-03-28 17:04:21 +01:00
Kamran Ahmed
db273210fd Fix broken build 2023-03-28 15:47:14 +01:00
Kamran Ahmed
9370e262c0 Add content for mongodb-aggreagtion 2023-03-28 15:35:11 +01:00
Kamran Ahmed
e0f9bc8456 Add mongodb content 2023-03-28 14:05:57 +01:00
Kamran Ahmed
af211ab129 Add related roadmaps 2023-03-27 23:12:06 +01:00
Kamran Ahmed
eab1fb31b2 Fix typo 2023-03-27 21:25:50 +01:00
Kamran Ahmed
c36fd71ec1 Add content for cyber-security roadmap 2023-03-27 21:20:39 +01:00
Kamran Ahmed
7d0e35d7ae Update wireframing tools 2023-03-27 14:40:35 +01:00
Kamran Ahmed
59d881a77b Update ux design roadmap description 2023-03-27 13:43:27 +01:00
Kamran Ahmed
b57e3ecc75 Add content for UX Design Roadmap 2023-03-27 13:26:14 +01:00
Kamran Ahmed
cd5c0c10a2 Add content to UX Design Roadmap 2023-03-27 12:49:19 +01:00
Kamran Ahmed
10a5e4c0ae Add content to UX Design Roadmap 2023-03-27 12:41:38 +01:00
Kamran Ahmed
432983631d Add content to UX Design Roadmap 2023-03-27 12:39:02 +01:00
Kamran Ahmed
29189062b9 Add content to UX Design Roadmap 2023-03-27 06:04:38 +01:00
Kamran Ahmed
84138d5049 Update prompt 2023-03-27 04:38:47 +01:00
Kamran Ahmed
c28ac4b078 Add content writing command 2023-03-27 04:11:34 +01:00
Kamran Ahmed
66bdbd7458 Automate the title creation in new roadmap content 2023-03-27 03:44:54 +01:00
Kamran Ahmed
907f820778 Rename roadmap/best-practice content to dirs 2023-03-27 03:08:19 +01:00
Kamran Ahmed
ec0a8a99ef Add content to UX roadmap 2023-03-27 03:04:34 +01:00
Kamran Ahmed
a7a342c8e7 Add link to UX Design Roadmap 2023-03-26 22:03:50 +01:00
Kamran Ahmed
e0e26580fa Add UX design roadmap pdf 2023-03-26 21:45:49 +01:00
Kamran Ahmed
f9d96d415f Add ux-design roadmap 2023-03-26 21:45:49 +01:00
github-actions[bot]
e1c22932be chore: update dependencies to latest (#3720)
Co-authored-by: kamranahmedse <kamranahmedse@users.noreply.github.com>
2023-03-26 00:14:53 +00:00
Chandresh Patidar
9dae1b3595 Update 114-never.md (#3716)
Replace a deprecated link with new link for `never` type documentation
2023-03-25 15:40:07 +00:00
Kamran Ahmed
cdb642c8d4 Update newsletter 2023-03-25 04:10:40 +00:00
Arik Chakma
71b43af862 fix: upcoming button overlap (#3713)
* docs: new resources for jsx and functional components

* fix: upcoming button overlap
2023-03-24 15:08:17 +00:00
github-actions[bot]
69dccb3fcc chore: update dependencies to latest (#3677)
Co-authored-by: kamranahmedse <kamranahmedse@users.noreply.github.com>
2023-03-21 23:45:15 +00:00
Akshay Jagiasi
ca6ddb4654 How Bitcoin Blockchain actually work video added (#3684)
* How Bitcoin Blockchain actually work video added

* Delete package-lock.json
2023-03-21 23:44:28 +00:00
Ivan Kibala
0ac6fc70ff Fix typo (#3686)
Fix typo
2023-03-21 23:42:54 +00:00
Sandro Fiorio de S. Júnior
5ddb021898 Add content to QA roadmap (#3688)
Co-authored-by: Sandro Fiorio <sandrofioriojr@gmail.com>
2023-03-21 23:42:38 +00:00
Julio Martins
d227603a59 feat: added description and content at FastAPI on Python Roadmap (#3678) 2023-03-20 19:41:49 +00:00
Yerkebulan
e0d70950ac Fix ORM title (#3682)
Update title. Orms to ORMs
2023-03-20 19:39:28 +00:00
Kamran Ahmed
148bfd8736 Add guide on jump servers 2023-03-20 19:37:31 +00:00
Kamran Ahmed
648985cefc Update font and page size for guide page 2023-03-20 18:13:13 +00:00
Kamran Ahmed
9321ac6aa1 Add guide on jump servers 2023-03-20 17:46:22 +00:00
Kamran Ahmed
170ab3a6cf Update git resources 2023-03-20 17:46:22 +00:00
Shreyas Karanam
708fa31998 Fix typo in FAQ (#3658)
Removed multiple AWS listings
2023-03-18 21:41:47 +00:00
Aroyan
f83a1a6c3b Update links to the latest React documentation (#3675)
* feat: add Set JavaScript content

* refactor: update links to the latest react documentation

This commit updates all links that referred to outdated(legacy) React documentation to the latest version
2023-03-18 20:39:05 +00:00
Lane Wagner
dc1d7ef226 Add the complete Docker course by Boot.dev to back-end and devops roadmaps (#3676) 2023-03-18 20:38:12 +00:00
Aroyan
808bd40cce fix: update react docs to new url (#3656)
* feat: add Set JavaScript content

* fix: update react docs and remove beta docs

* update react docs url
2023-03-17 11:15:36 +00:00
Kamran Ahmed
45ce59b10d Add guide for semantic HTML 2023-03-17 02:47:27 +00:00
wagslane
e3f41ec0e3 add updated HTTP networking course 2023-03-16 20:33:55 +00:00
Kamran Ahmed
4f821d0f1d Add PDF and image for MongoDB roadmap 2023-03-16 20:32:14 +00:00
kamranahmedse
ec1283a5dc chore: update sponsors 2023-03-16 20:27:49 +00:00
Kamran Ahmed
4da909d358 Add MongoDB roadmap link 2023-03-16 20:27:19 +00:00
Kamran Ahmed
0beb9ad239 Add MongoDB roadmap 2023-03-16 20:23:47 +00:00
Kamran Ahmed
c6213dde92 Update guides 2023-03-16 18:36:05 +00:00
Kamran Ahmed
3d655965f6 Add event tracking for done/pending 2023-03-16 02:09:26 +00:00
Kamran Ahmed
c5c2ee3b2c Update events 2023-03-16 01:54:41 +00:00
Kamran Ahmed
cad0813eb6 Add event for roadmap switch 2023-03-16 01:05:11 +00:00
Kamran Ahmed
f9c1e6e0a2 Add event tracking for topic load 2023-03-16 00:58:10 +00:00
Kamran Ahmed
d3578756d4 Add event tracking for topic load 2023-03-16 00:53:02 +00:00
Kamran Ahmed
5fe506324a Add article for secrets management 2023-03-15 23:05:33 +00:00
Kamran Ahmed
bc007dcc9b Add article for DevOps language 2023-03-15 23:04:22 +00:00
Arthur Henrique
42a5d5bba6 feat: Include atlassian tutorials in git section (#3557)
Atlassian's Git Tutorials come across the beginning up to advanced tips on git usage, a great place to dive into its understanding
2023-03-14 08:56:02 +00:00
The New Stack
f908c5371d Added TNS K8s Overview Page (#3558) 2023-03-14 08:53:46 +00:00
The New Stack
78964b9f65 Added TNS K8s Primer Article (#3559) 2023-03-14 08:53:25 +00:00
The New Stack
9f11de60ed Added TNS K8s Explainer YouTube Video (#3560) 2023-03-14 08:53:09 +00:00
The New Stack
4ba28b702b Added 2 TNS K8s Alternative Resources (#3561) 2023-03-14 08:52:39 +00:00
The New Stack
45c6fc873f Added TNS K8s Deployment Tutorial (#3562) 2023-03-14 08:52:15 +00:00
The New Stack
23b7c21502 Added TNS Article for Cluster Deployment on Ubuntu (#3564) 2023-03-14 08:52:01 +00:00
The New Stack
d474d07ebb Added 2 TNS Articles on K8s Managed Services (#3563) 2023-03-14 08:47:09 +00:00
The New Stack
7d08572d78 Added 2 TNS Articles Covering K8s Pods (#3565) 2023-03-14 08:46:28 +00:00
The New Stack
17fc85f893 Added TNS Article for K8s Sets (#3566) 2023-03-14 08:45:56 +00:00
github-actions[bot]
5ccba8d7c0 chore: update dependencies to latest (#3576)
Co-authored-by: kamranahmedse <kamranahmedse@users.noreply.github.com>
2023-03-14 08:45:14 +00:00
Alex Ivanovs
fefbb4f833 Updating resources for React Router (#3586)
I'm adding a link to a React Router cheatsheet/reference covering examples of various routes, including core components and routers introduced in v6.4.
2023-03-14 08:42:26 +00:00
The New Stack
bcbc9c9d54 Added TNS Article for Building Stateful K8s Applications (#3587) 2023-03-14 08:40:47 +00:00
The New Stack
73d1d0e389 Added TNS Article About K8s as Universal Schedular (#3588) 2023-03-14 08:40:25 +00:00
The New Stack
14856560c3 Added TNS Article on K8s Ingress for Beginners (#3589) 2023-03-14 08:39:56 +00:00
The New Stack
b097395c07 Added TNS Article on Ingress Controllers for K8s (#3590) 2023-03-14 08:39:29 +00:00
The New Stack
929be729e5 Added TNS Article on K8s Networking (#3591) 2023-03-14 08:39:07 +00:00
The New Stack
1eb8fab15e Added TNS Article on CRD and ConfigMaps (#3592) 2023-03-14 08:38:49 +00:00
The New Stack
51233c8011 Added TNS Article on Secrets Management (#3593) 2023-03-14 08:38:26 +00:00
The New Stack
0081e9059c Added 2 TNS Articles on Resource Types, Requests, Limits (#3594) 2023-03-14 08:38:02 +00:00
The New Stack
4e66148777 Added TNS Article on Namespaces (#3595) 2023-03-14 08:37:48 +00:00
The New Stack
18e430be0b Added TNS Article on Choosing Monitoring Tools (#3596) 2023-03-14 08:37:22 +00:00
The New Stack
e39d0d93e0 Added 5 TNS Articles on K8s Access Control (#3605) 2023-03-14 08:37:05 +00:00
The New Stack
9260dc36b5 Added 3 TNS Articles on K8s Network Security (#3606) 2023-03-14 08:36:16 +00:00
The New Stack
f5f846ed73 Added 2 TNS Articles on Container and Pod Security (#3607) 2023-03-14 08:35:55 +00:00
The New Stack
7518d60013 Added 2 TNS Articles on K8s Security Scanning (#3608) 2023-03-14 08:35:31 +00:00
Kamran Ahmed
59d9674d75 Add guide about SSL certificates 2023-03-13 21:49:49 +00:00
Kota Hayashi
16fb03086e Add riverpod desc (#3599)
* Add riverpod desc

* Update src/data/roadmaps/flutter/content/111-state-management/102-riverpod.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2023-03-13 20:37:19 +00:00
Kamran Ahmed
c0f46c5eed Add support for ads on best-practices 2023-03-13 18:04:20 +00:00
Kamran Ahmed
b5b8b92791 Update sponsors workflow 2023-03-13 18:02:15 +00:00
kamranahmedse
8f90dac32e chore: update sponsors 2023-03-13 17:44:48 +00:00
Kamran Ahmed
e9f3a616d1 Add FAQs for DevOps 2023-03-10 01:05:41 +00:00
Kamran Ahmed
c28ed87247 Compress json 2023-03-09 17:07:04 +00:00
Kamran Ahmed
53fb6313db Add link to AWS best practices 2023-03-09 17:05:42 +00:00
Kamran Ahmed
cc933b238d Add best practices for AWS 2023-03-09 16:54:50 +00:00
github-actions[bot]
48fba932b4 chore: update dependencies to latest (#3530)
Co-authored-by: kamranahmedse <kamranahmedse@users.noreply.github.com>
2023-03-06 18:20:40 +00:00
Kamran Ahmed
9961259ffb Fix typo 2023-03-04 23:58:23 +00:00
Kamran Ahmed
cab2054c1d Add related roadmaps to each roadmap page 2023-03-04 23:55:14 +00:00
Kamran Ahmed
d34affb420 Add related roadmaps to each roadmap page 2023-03-04 23:51:40 +00:00
Kamran Ahmed
ee4f0980bc Link to best practices from roadmaps 2023-03-04 18:43:28 +00:00
Kamran Ahmed
37fdd010a8 Add Cyber Security roadmap 2023-03-04 18:28:18 +00:00
Kamran Ahmed
aa04c51a12 Add PDF url 2023-03-04 18:27:35 +00:00
Kamran Ahmed
7993f12d12 Add content for certs and ctfs 2023-03-04 18:27:03 +00:00
Kamran Ahmed
1a3265295c Add directories for content 2023-03-04 18:27:02 +00:00
Kamran Ahmed
238245431b Add cyber-security roadmap 2023-03-04 18:27:02 +00:00
rakibulhaq
48c04055d5 Fix - Typo in 3rd line of migration strategies (#3522)
Solves the issue #3520
2023-03-03 09:00:15 +00:00
Kamran Ahmed
596b8f56ac Update frontend beginner roadmap 2023-03-02 23:34:53 +00:00
Kamran Ahmed
45267693e2 Addevent on switch 2023-03-02 22:13:25 +00:00
Kamran Ahmed
f932df8627 Use query parameters for roadmap switching 2023-03-02 22:05:58 +00:00
Kamran Ahmed
8dcf4b00c4 Refactor frontend roadmap for beginners 2023-03-02 21:58:14 +00:00
Kamran Ahmed
cb32a9610d Add beginner version of frontend roadmap 2023-03-02 19:19:34 +00:00
Kamran Ahmed
01c090f62d Update renderer 2023-03-02 15:41:49 +00:00
Kamran Ahmed
60b1edcab9 Update event labels for sponsor links 2023-03-01 23:04:32 +00:00
Kamran Ahmed
d08887060f Update sponsor workflow title 2023-03-01 02:39:52 +00:00
Kamran Ahmed
24a6c4930e Update sponsors workflow 2023-03-01 02:31:18 +00:00
Kamran Ahmed
e57b889f73 Add script to update sponsors 2023-03-01 02:28:46 +00:00
Kamran Ahmed
c5d14d2543 Update dependencies 2023-03-01 02:22:49 +00:00
Kamran Ahmed
4f0b08ea93 Add sponsor action 2023-03-01 02:19:56 +00:00
github-actions[bot]
47e2dbdd12 chore: update dependencies to latest (#3445)
Co-authored-by: kamranahmedse <kamranahmedse@users.noreply.github.com>
2023-03-01 01:27:31 +00:00
Kamran Ahmed
f1ad70acd9 Update backend, js and python 2023-03-01 00:40:28 +00:00
Kamran Ahmed
ac230bbf29 Add sponsor links on the roadmaps 2023-03-01 00:40:28 +00:00
prchann
d0861711ac Fix a link issue in markdown (#3508) 2023-02-28 19:20:46 +00:00
Amin Rezaei
74b2dda7f7 Fix heading for content file (#3509)
Removed unnecessary heading <code> tag.
2023-02-28 19:17:36 +00:00
Kamran Ahmed
2b49fa3182 Add responsive widgets 2023-02-28 19:16:46 +00:00
Kamran Ahmed
e2a8240e35 Add FVM 2023-02-28 19:12:05 +00:00
Kamran Ahmed
a7f45c0af1 Fix some flutter nodes not clickable 2023-02-28 17:31:39 +00:00
Kamran Ahmed
77a6270bd7 Update flutter roadmap 2023-02-28 17:29:47 +00:00
Kamran Ahmed
64d3ad662c Add riverpod and remove getx 2023-02-28 17:18:41 +00:00
Kamran Ahmed
c8b8e12b64 Add guide for database setup 2023-02-27 10:56:39 +00:00
Tobias Uhmann
8f94a5887e Update 101-jdbc-template.md (#3478)
Clarify in the first sentence that "the JDBC core package" is about Spring
2023-02-25 18:49:54 +00:00
Aroyan
00b6217e63 feat: add urql content (#3483) 2023-02-25 18:49:33 +00:00
João
1a0d7463eb Add Interactive Git Branching Guide by pcottle (#3486)
Github repo of added resource: https://github.com/pcottle/learnGitBranching
2023-02-25 18:49:20 +00:00
Olusola Amoo
983ee44632 fix(blockchain-content): Fix typo in roadmaps > blockchain > security > tools content (#3489) 2023-02-25 18:49:03 +00:00
Olusola Amoo
f393cb186e fix(blockchain-content): fix typo in blockchain solidity content (#3490) 2023-02-25 18:48:45 +00:00
devwithsmf
70edfb0ac2 Adding content to Kubernetes Roadmap (#3477)
* Update 100-kubernetes-overview.md

* Update 101-why-kubernetes.md

* Update 102-key-concepts-terminologies.md

* Update 103-kubernetes-alternatives.md

* Update index.md

* Update 100-installing-a-local-cluster.md

* Update 101-why-kubernetes.md

* Update 102-key-concepts-terminologies.md

* Update 103-kubernetes-alternatives.md

* Update index.md

* Update 100-installing-a-local-cluster.md

* Update 101-choosing-a-managed-provider.md

* Update 102-deploying-your-first-application.md

* Update index.md

* Update 100-pods.md

* Update 101-replicasets.md

* Update 103-statefulsets.md

* Update 103-statefulsets.md

* Update 104-jobs.md

* Update index.md

* Update 100-networking-and-pod-to-pod-communication.md

* Update 101-load-balancing.md

* Update 102-external-access-to-services.md

* Update index.md

* Update 100-config-maps.md

* Update 101-secrets.md

* Update index.md

* Update 100-setting-resource-requests-and-limits.md

* Update 101-assigning-quotas-to-namespaces.md

* Update 102-monitoring-and-optimizing-resource-usage.md

* Update 102-monitoring-and-optimizing-resource-usage.md

* Update index.md

* Update 100-role-based-acccess-control.md

* Update 101-network-security.md

* Update 102-container-and-pod-security.md

* Update 103-security-scanners.md

* Update index.md

* Update 100-logs.md

* Update 101-metrics.md

* Update 102-traces.md

* Update 103-resource-health.md

* Update 104-observability-engines.md

* Update index.md

* Update 100-horizontal-pod-autoscaler.md

* Update 101-vertical-pod-autoscaler.md

* Update 102-cluster-autoscaling.md

* Update index.md

* Update 100-scheduling-basics.md

* Update 101-taints-and-tolerations.md

* Update 102-topology-spread-constraints.md

* Update 103-pod-priorities.md

* Update 104-evictions.md

* Update index.md

* Update 100-csi-drivers.md

* Update 101-stateful-applications.md

* Update index.md

* Update 100-ci-cd-integration.md

* Update 101-gitops.md

* Update 102-helm-charts.md

* Update 103-canary-deployments.md

* Update 104-blue-green-deployments.md

* Update 105-rolling-updates-rollbacks.md

* Update index.md

* Update 100-custom-controllers.md

* Update 101-custom-schedulers-extenders.md

* Update 102-custom-resource-definitions.md

* Update 103-kubernetes-extensions-and-apis.md

* Update 104-own-cluster.md

* Update 105-control-plane-installation.md

* Update 106-managing-worker-nodes.md

* Update 107-multi-cluster-management.md

* Update index.md

* Update index.md
2023-02-25 18:46:21 +00:00
Bernd Hobbie
ed07d34d64 Move 'exclude' option in tsconfig.json out of 'compilerOptions (#3479) 2023-02-25 18:44:43 +00:00
Appasaheb Nage
831521ae10 Update tsconfig (#3496) 2023-02-25 18:44:12 +00:00
Kamran Ahmed
e29289f0dc Add API Security PDF 2023-02-22 09:48:13 +00:00
Kamran Ahmed
0fd3eb0cc6 Add link to API Security Best Practices 2023-02-21 19:41:54 +00:00
Kamran Ahmed
f58a77010b Add content for API security best practices 2023-02-21 19:37:35 +00:00
Kamran Ahmed
6303e31c0e Add content for API security best practices 2023-02-21 19:22:15 +00:00
Kamran Ahmed
dfc2d39427 Add content to API security best practices 2023-02-21 15:48:03 +00:00
Kamran Ahmed
5e75026424 Add content to API security best practices 2023-02-21 15:39:34 +00:00
Kamran Ahmed
7a4c077a90 Add content for API security best practices 2023-02-21 15:16:15 +00:00
Kamran Ahmed
e45c49a404 Fix broken build 2023-02-21 12:44:44 +00:00
Kamran Ahmed
b6a0255f12 Change location for data files 2023-02-21 12:25:58 +00:00
Kamran Ahmed
b741a0e1ee Add support for link-groups 2023-02-20 16:59:02 +00:00
Kamran Ahmed
8200993af4 Update subscription 2023-02-14 14:07:49 +00:00
Kamran Ahmed
5c1d803892 Fix mistake in constructor overloading 2023-02-13 14:19:30 +00:00
Natan Yellin
dcf0f94af9 Update 101-containers.md (#3440) 2023-02-10 21:03:58 +00:00
Artem Gontar
4ad8886aa0 fix: broken link to Circuit Breaker AWS WAF (#3304) 2023-02-10 18:39:44 +00:00
Lucas Soares
a1143cd6cb Add content to Node.js roadmap (#3305) 2023-02-10 18:38:06 +00:00
mohd-e-mustafa
f130c706da Add filtering content (#3355)
* Updated 100-filtering.md added content

* Update content

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2023-02-10 18:31:23 +00:00
mohd-e-mustafa
8068face54 Add rate-limiting content (#3364)
* Update 101-rate-limiting.md

* Update src/roadmaps/angular/content/101-rxjs-basics/104-operators/101-rate-limiting.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2023-02-10 18:27:15 +00:00
Benson Arafat
39866117a6 Update Nuxt resources (#3365)
The current version of nuxt is v3.
Update source to v3
2023-02-10 18:25:32 +00:00
mohd-e-mustafa
df7aa17f86 Add content for RxJS transformation (#3369) 2023-02-10 18:22:45 +00:00
payal pagariya
ee6572660b Add resources for data structures (#3374)
Added resources related to stack, queue and recursion all implemented in JavaScript
2023-02-10 18:20:56 +00:00
Jorge Martin
9875a2d6f7 Fix spelling mistake (#3378) 2023-02-10 18:15:45 +00:00
Nikita Monastyrskiy
5b180e2597 Add content to globby (#3432) 2023-02-10 18:10:39 +00:00
Nikita Monastyrskiy
6f05972493 Add content to fs-extra (#3433) 2023-02-10 18:10:15 +00:00
Felipe Riveras
4bd182e4d0 Add MDN resources for hosting (#3436)
this articles explain very good the term and explain with clarity
2023-02-10 18:08:30 +00:00
Israni, Murli
68d319cacb Fix spelling mistake (#3439) 2023-02-10 18:08:00 +00:00
Mohammad Morakabati
3e76df8d2a Add gRPC resources (#3429) 2023-02-10 18:07:37 +00:00
Kamran Ahmed
9d69477947 Fix broken URLs 2023-02-10 17:55:18 +00:00
Kamran Ahmed
e0ead47fb1 Add kubernetes roadmap 2023-02-10 15:49:39 +00:00
Kamran Ahmed
253c88542f Upgrade dependencies 2023-02-10 15:49:39 +00:00
andran777
5bca9834fb Update Spring Boot scope content (#3398) 2023-02-08 17:42:45 +00:00
Jesse Chang
dde6e3d3df Remove beego link (#3403)
* Update 100-beego.md

New official documentation website.

* Update src/roadmaps/golang/content/104-go-web-frameworks/100-beego.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2023-02-08 16:12:05 +00:00
Khushi Kothari
6fe8fee25f Add MDN link to Asynchronous JavaScript (#3404)
* Update index.md

* Update src/roadmaps/javascript/content/112-javascript-asynchronous-javascript/index.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2023-02-08 16:09:55 +00:00
Hans Elde
f3622a1b1c Fix the availability numbers (#3410)
* Update 102-availability-in-numbers.md

Updated downtime #s using https://uptime.is/

* Update src/roadmaps/system-design/content/105-availability-patterns/102-availability-in-numbers.md

* Update src/roadmaps/system-design/content/105-availability-patterns/102-availability-in-numbers.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2023-02-08 16:00:25 +00:00
Muhammad Kamal Fergany
a92bda38f4 Fix a typo in 103-load-balancer.md (#3414) 2023-02-08 15:58:18 +00:00
Kamran Ahmed
b194d167be Make youtube banner non-sticky 2023-02-08 12:29:30 +00:00
Kamran Ahmed
ec04b582a6 Add note for feedback 2023-02-08 12:22:09 +00:00
Kamran Ahmed
f55159a12b Update title for kubernetes page 2023-02-08 11:19:58 +00:00
Kamran Ahmed
938c7796d1 Update title for best practices pages 2023-02-08 11:19:58 +00:00
Paul Reichetanz
e04bd9db05 Fix typos in frontend/123-bonus-content.md (#3423) 2023-02-08 11:00:30 +00:00
Kamran Ahmed
7c837d14da Add link to Kubernetes roadmap 2023-02-07 20:59:11 +00:00
Kamran Ahmed
cc05587d9e Add kubernetes roadmap 2023-02-07 20:49:14 +00:00
Kamran Ahmed
2172014d6e Add dimensions 2023-02-07 20:21:03 +00:00
Kamran Ahmed
98d43e76b7 Add kubernetes roadmap 2023-02-07 20:19:39 +00:00
Kamran Ahmed
7665970813 Add kubecampus snippet 2023-02-06 21:55:55 +00:00
Kamran Ahmed
d96e5890b9 Add content to TypeScript roadmap 2023-02-06 21:26:56 +00:00
Kamran Ahmed
659bd93094 Add content to TypeScript roadmap 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
a4dddfb19b Adding content to 101-typescript-types 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
12a4be2227 Adding content to 104-undefined 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
9edcb35acb Adding content to 115-type-assertions 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
1df4e4b836 Adding content to 111-utility-types 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
49e78cf1c0 102-type-inference, 103-type-compatibility, 110-decorators 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
a4a29b4efa 114-ecosystem 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
3e49e7f91d 113-modules 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
7627bc73b5 112-advanced-types 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
26eaa40dc1 Adding content to 109-generics 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
45a0b53d5f Adding content to 108-classes 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
7bac3c3444 Adding content to 107-interfaces 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
62905bda7a Adding content to 106-functions 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
179bf366cc Adding content to 104-combining-types 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
59d47c5b1e Adding content to 100-typescript 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
d23ea8e577 Adding content to 103-running-typescript 2023-02-06 21:26:56 +00:00
syedmouaazfarrukh
07f001f8be Adding content to 102-install-configure 2023-02-06 21:26:56 +00:00
Kamran Ahmed
754a91acef Fix invalid link 2023-02-06 15:09:55 +00:00
Kamran Ahmed
16c550211b Fix invalid link 2023-02-06 15:08:33 +00:00
Kamran Ahmed
a56710c43d Remove ambassador and tigera links 2023-02-02 00:11:20 +04:00
CodeGuage
00f94e031e Add link to switch in JS Roadmap (#3380) 2023-01-31 20:56:37 +04:00
Tianzhou (天舟)
d1556c85df docs: add PostGIS and Bytebase to the postgresql-dba roadmap. (#3390) 2023-01-31 20:55:55 +04:00
tim-laue
1885d6d304 Fix broken link (#3381)
Fixed broken link
2023-01-31 20:55:13 +04:00
Zanin Andrea
3b8c8316b3 Add HTTPs content (#3384)
I have added a brief explanation of how the HTTPS protocol works and a new resource to learn more
2023-01-31 20:54:30 +04:00
mohd-e-mustafa
034fd16a1f Add combination content in RxJS (#3385) 2023-01-31 20:53:24 +04:00
Diego Kfuri
aa9bf2f263 fix typo in system designs roadmap (#3395) 2023-01-31 20:52:12 +04:00
syedmouaazfarrukh
6a5df98f4f Add content to flutter roadmap (#3389)
* Adding content to 100-dart-basics

* Adding content to 102-setup-development-environment

* Adding content to 102-styled-widgets

* Adding content to 102-widgets

* Adding content to 103-working-with-assets

* Adding content to 105-repo-hosting-services

* Adding content to 106-design-principles

* Adding content to 107-package-manager

* Adding content to 108-working-with-apis

* Adding content to 102-firebase

* Adding content to 109-storage

* Adding content to 114-reactive programming

* Adding content to 115-dev-tools

* Adding content to 116-flutter-internals

* Adding content to 117-ci-cd

* Adding content to 118-analytics

* Adding content to 119-deployment

* Adding content to 110-advanced-dart

* Adding content to 111-state-management

* Adding content to 112-animations

* Adding content to 113-testing

* Adding content to 102-flutter-bloc, 101-flutter-outline

* Update src/roadmaps/flutter/content/100-dart-basics/104-operators.md

* Update src/roadmaps/flutter/content/101-setup-development-environment/101-ides/index.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2023-01-31 20:49:28 +04:00
Kamran Ahmed
ea02c8835a Add typescript roadmap reference in frontend roadmap 2023-01-31 14:23:18 +04:00
Kamran Ahmed
e13733a503 Add link to TypeScript roadmap 2023-01-31 04:28:24 +04:00
Kamran Ahmed
6f0ad58764 Add TypeScript roadmap (#3391)
* Add typescript roadmap

* Add typescript content

* Add typescript roadmap
2023-01-31 04:05:46 +04:00
Kamran Ahmed
f68c303ffa Fix canonicals on best-practices topic page 2023-01-31 01:19:54 +04:00
Kamran Ahmed
b2c79ff395 Fix canonicals on topic pages 2023-01-31 01:15:13 +04:00
3856 changed files with 33562 additions and 7855 deletions

43
.github/workflows/update-sponsors.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Update Sponsors
on:
workflow_dispatch: # allow manual run
schedule:
- cron: '0 0 * * *' # run daily at 00:00 UTC
env:
SPONSOR_SHEET_API_KEY: ${{ secrets.SPONSOR_SHEET_API_KEY }}
SPONSOR_SHEET_ID: ${{ secrets.SPONSOR_SHEET_ID }}
jobs:
update-sponsors:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: pnpm/action-setup@v2.2.2
with:
version: 7.13.4
- name: Install dependencies
run: |
pnpm install
- name: Update sponsors
run: |
node bin/update-sponsors.cjs
- name: Create PR
uses: peter-evans/create-pull-request@v4
with:
delete-branch: false
branch: 'update-sponsors'
base: 'master'
labels: |
sponsors
automated pr
reviewers: kamranahmedse
commit-message: 'chore: update sponsors'
title: 'Update Sponsor Banners'
body: |
Updates sponsor banners.
Please review the changes and merge if everything looks good.

7
.prettierignore Normal file
View File

@@ -0,0 +1,7 @@
app-dist
dist
.idea
.github
public
node_modules
pnpm-lock.yaml

18
.prettierrc.cjs Normal file
View File

@@ -0,0 +1,18 @@
module.exports = {
semi: true,
singleQuote: true,
overrides: [
{
files: '*.astro',
options: {
parser: 'astro',
singleQuote: true,
jsxSingleQuote: true,
},
},
],
plugins: [
require.resolve('prettier-plugin-astro'),
require('prettier-plugin-tailwindcss'),
],
};

6
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"prettier.documentSelectors": ["**/*.astro"],
"[astro]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

View File

@@ -7,7 +7,7 @@ import rehypeExternalLinks from 'rehype-external-links';
import { serializeSitemap, shouldIndexPage } from './sitemap.mjs';
export default defineConfig({
site: 'https://roadmap.sh',
site: 'https://roadmap.sh/',
markdown: {
shikiConfig: {
theme: 'dracula',
@@ -17,6 +17,24 @@ export default defineConfig({
rehypeExternalLinks,
{
target: '_blank',
rel: function (element) {
const href = element.properties.href;
const whiteListedStarts = [
'/',
'#',
'mailto:',
'https://github.com/kamranahmedse',
'https://thenewstack.io',
'https://cs.fyi',
'https://roadmap.sh',
];
if (whiteListedStarts.some((start) => href.startsWith(start))) {
return [];
}
return 'noopener noreferrer nofollow';
},
},
],
],

View File

@@ -3,7 +3,10 @@ const path = require('path');
const CONTENT_DIR = path.join(__dirname, '../content');
// Directory containing the best-practices
const BEST_PRACTICE_CONTENT_DIR = path.join(__dirname, '../src/best-practices');
const BEST_PRACTICE_CONTENT_DIR = path.join(
__dirname,
'../src/data/best-practices'
);
const bestPracticeId = process.argv[2];
const allowedBestPracticeId = fs.readdirSync(BEST_PRACTICE_CONTENT_DIR);
@@ -28,7 +31,10 @@ if (!bestPracticeDirName) {
process.exit(1);
}
const bestPracticeDirPath = path.join(BEST_PRACTICE_CONTENT_DIR, bestPracticeDirName);
const bestPracticeDirPath = path.join(
BEST_PRACTICE_CONTENT_DIR,
bestPracticeDirName
);
const bestPracticeContentDirPath = path.join(
BEST_PRACTICE_CONTENT_DIR,
bestPracticeDirName,
@@ -37,7 +43,9 @@ const bestPracticeContentDirPath = path.join(
// If best practice content already exists do not proceed as it would override the files
if (fs.existsSync(bestPracticeContentDirPath)) {
console.error(`Best Practice content already exists @ ${bestPracticeContentDirPath}`);
console.error(
`Best Practice content already exists @ ${bestPracticeContentDirPath}`
);
process.exit(1);
}
@@ -51,7 +59,11 @@ function prepareDirTree(control, dirTree) {
const controlName = control?.properties?.controlName || '';
// No directory for a group without control name
if (!controlName || controlName.startsWith('check:') || controlName.startsWith('ext_link:')) {
if (
!controlName ||
controlName.startsWith('check:') ||
controlName.startsWith('ext_link:')
) {
return;
}
@@ -76,7 +88,10 @@ function prepareDirTree(control, dirTree) {
return { dirTree };
}
const bestPractice = require(path.join(__dirname, `../public/jsons/best-practices/${bestPracticeId}`));
const bestPractice = require(path.join(
__dirname,
`../public/jsons/best-practices/${bestPracticeId}`
));
const controls = bestPractice.mockup.controls.control;
// Prepare the dir tree that we will be creating
@@ -129,11 +144,7 @@ function createDirTree(parentDir, dirTree, filePaths = {}) {
// For each of the directory names, create a
// directory inside the given directory
childrenDirNames.forEach((dirName) => {
createDirTree(
path.join(parentDir, dirName),
dirTree[dirName],
filePaths
);
createDirTree(path.join(parentDir, dirName), dirTree[dirName], filePaths);
});
return filePaths;
@@ -141,4 +152,4 @@ function createDirTree(parentDir, dirTree, filePaths = {}) {
// Create directories and get back the paths for created directories
createDirTree(bestPracticeContentDirPath, dirTree);
console.log('Created best practice content directory structure');
console.log('Created best practice content directory structure');

View File

@@ -1,4 +1,5 @@
## CLI Tools
> A bunch of CLI scripts to make the development easier
## `roadmap-links.cjs`
@@ -9,12 +10,22 @@ Generates a list of all the resources links in any roadmap file.
Compresses all the JSON files in the `public/jsons` folder
## `update-sponsors.cjs`
Updates the sponsor ads on each roadmap page with the latest sponsor information in the Excel sheet.
## `roadmap-content.cjs`
Currently, for any new roadmaps that we add, we do create the interactive roadmap but we end up leaving the content empty in the roadmap till we get time to fill it up manually.
This script populates all the content files with some minimal content from OpenAI so that the users visiting the website have something to read in the interactive roadmap till we get time to fill it up manually.
## `roadmap-dirs.cjs`
This command is used to create the content folders and files for the interactivity of the roadmap. You can use the below command to generate the roadmap skeletons inside a roadmap directory:
```bash
npm run roadmap-content [frontend|backend|devops|...]
npm run roadmap-dirs [frontend|backend|devops|...]
```
For the content skeleton to be generated, we should have proper grouping, and the group names in the project files. You can follow the steps listed below in order to add the meta information to the roadmap.
@@ -24,5 +35,3 @@ For the content skeleton to be generated, we should have proper grouping, and th
- Assign the name to the groups.
- Group names have the format of `[sort]-[slug]` e.g. `100-internet`. Each group name should start with a number starting from 100 which helps with sorting of the directories and the files. Groups at the same level have the sequential sorting information.
- Each groups children have a separate group and have the name similar to `[sort]-[parent-slug]:[child-slug]` where sort refers to the sorting of the `child-slug` and not the parent. Also parent-slug does not need to have the sorting information as a part of slug e.g. if parent was `100-internet` the children would be `100-internet:how-does-the-internet-work`, `101-internet:what-is-http`, `102-internet:browsers`.

View File

@@ -1,12 +1,13 @@
const fs = require('fs');
const path = require('path');
const CONTENT_DIR = path.join(__dirname, '../content');
// Directory containing the roadmaps
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../src/roadmaps');
const OPEN_AI_API_KEY = process.env.OPEN_AI_API_KEY;
const ALL_ROADMAPS_DIR = path.join(__dirname, '../src/data/roadmaps');
const ROADMAP_JSON_DIR = path.join(__dirname, '../public/jsons/roadmaps');
const roadmapId = process.argv[2];
const allowedRoadmapIds = fs.readdirSync(ROADMAP_CONTENT_DIR);
const allowedRoadmapIds = fs.readdirSync(ALL_ROADMAPS_DIR);
if (!roadmapId) {
console.error('roadmapId is required');
process.exit(1);
@@ -18,146 +19,144 @@ if (!allowedRoadmapIds.includes(roadmapId)) {
process.exit(1);
}
// Directory holding the roadmap content files
const roadmapDirName = fs
.readdirSync(ROADMAP_CONTENT_DIR)
.find((dirName) => dirName.replace(/\d+-/, '') === roadmapId);
if (!roadmapDirName) {
console.error('Roadmap directory not found');
process.exit(1);
}
const roadmapDirPath = path.join(ROADMAP_CONTENT_DIR, roadmapDirName);
const roadmapContentDirPath = path.join(
ROADMAP_CONTENT_DIR,
roadmapDirName,
'content'
);
// If roadmap content already exists do not proceed as it would override the files
if (fs.existsSync(roadmapContentDirPath)) {
console.error(`Roadmap content already exists @ ${roadmapContentDirPath}`);
process.exit(1);
}
function prepareDirTree(control, dirTree, dirSortOrders) {
// Directories are only created for groups
if (control.typeID !== '__group__') {
return;
}
// e.g. 104-testing-your-apps:other-options
const controlName = control?.properties?.controlName || '';
// e.g. 104
const sortOrder = controlName.match(/^\d+/)?.[0];
// No directory for a group without control name
if (!controlName || !sortOrder) {
return;
}
// e.g. testing-your-apps:other-options
const controlNameWithoutSortOrder = controlName.replace(/^\d+-/, '');
// e.g. ['testing-your-apps', 'other-options']
const dirParts = controlNameWithoutSortOrder.split(':');
// Nest the dir path in the dirTree
let currDirTree = dirTree;
dirParts.forEach((dirPart) => {
currDirTree[dirPart] = currDirTree[dirPart] || {};
currDirTree = currDirTree[dirPart];
});
dirSortOrders[controlNameWithoutSortOrder] = Number(sortOrder);
const childrenControls = control.children.controls.control;
// No more children
if (childrenControls.length) {
childrenControls.forEach((childControl) => {
prepareDirTree(childControl, dirTree, dirSortOrders);
});
}
return { dirTree, dirSortOrders };
}
const roadmap = require(path.join(__dirname, `../public/jsons/roadmaps/${roadmapId}`));
const controls = roadmap.mockup.controls.control;
// Prepare the dir tree that we will be creating and also calculate the sort orders
const dirTree = {};
const dirSortOrders = {};
controls.forEach((control) => {
prepareDirTree(control, dirTree, dirSortOrders);
const ROADMAP_CONTENT_DIR = path.join(ALL_ROADMAPS_DIR, roadmapId, 'content');
const { Configuration, OpenAIApi } = require('openai');
const configuration = new Configuration({
apiKey: OPEN_AI_API_KEY,
});
/**
* @param parentDir Parent directory in which directory is to be created
* @param dirTree Nested dir tree to be created
* @param sortOrders Mapping from groupName to sort order
* @param filePaths The mapping from groupName to file path
*/
function createDirTree(parentDir, dirTree, sortOrders, filePaths = {}) {
const childrenDirNames = Object.keys(dirTree);
const hasChildren = childrenDirNames.length !== 0;
const openai = new OpenAIApi(configuration);
// @todo write test for this, yolo for now
const groupName = parentDir
.replace(roadmapContentDirPath, '') // Remove base dir path
.replace(/(^\/)|(\/$)/g, '') // Remove trailing slashes
.replace(/(^\d+?-)/g, '') // Remove sorting information
.replaceAll('/', ':') // Replace slashes with `:`
.replace(/:\d+-/, ':');
function getFilesInFolder(folderPath, fileList = {}) {
const files = fs.readdirSync(folderPath);
const humanizedGroupName = groupName
.split(':')
.pop()
?.replaceAll('-', ' ')
.replace(/^\w/, ($0) => $0.toUpperCase());
files.forEach((file) => {
const filePath = path.join(folderPath, file);
const stats = fs.statSync(filePath);
const sortOrder = sortOrders[groupName] || '';
if (stats.isDirectory()) {
getFilesInFolder(filePath, fileList);
} else if (stats.isFile()) {
const fileUrl = filePath
.replace(ROADMAP_CONTENT_DIR, '') // Remove the content folder
.replace(/\/\d+-/g, '/') // Remove ordering info `/101-ecosystem`
.replace(/\/index\.md$/, '') // Make the `/index.md` to become the parent folder only
.replace(/\.md$/, ''); // Remove `.md` from the end of file
// Attach sorting information to dirname
// e.g. /roadmaps/100-frontend/content/internet
// ———> /roadmaps/100-frontend/content/103-internet
if (sortOrder) {
parentDir = parentDir.replace(/(.+?)([^\/]+)?$/, `$1${sortOrder}-$2`);
}
// If no children, create a file for this under the parent directory
if (!hasChildren) {
let fileName = `${parentDir}.md`;
fs.writeFileSync(fileName, `# ${humanizedGroupName}`);
filePaths[groupName || 'home'] = fileName.replace(CONTENT_DIR, '');
return filePaths;
}
// There *are* children, so create the parent as a directory
// and create `index.md` as the content file for this
fs.mkdirSync(parentDir);
let readmeFilePath = path.join(parentDir, 'index.md');
fs.writeFileSync(readmeFilePath, `# ${humanizedGroupName}`);
filePaths[groupName || 'home'] = readmeFilePath.replace(CONTENT_DIR, '');
// For each of the directory names, create a
// directory inside the given directory
childrenDirNames.forEach((dirName) => {
createDirTree(
path.join(parentDir, dirName),
dirTree[dirName],
dirSortOrders,
filePaths
);
fileList[fileUrl] = filePath;
}
});
return filePaths;
return fileList;
}
// Create directories and get back the paths for created directories
createDirTree(roadmapContentDirPath, dirTree, dirSortOrders);
console.log('Created roadmap content directory structure');
function writeTopicContent(currTopicUrl) {
const [parentTopic, childTopic] = currTopicUrl
.replace(/^\d+-/g, '/')
.replace(/:/g, '/')
.replace(/^\//, '')
.split('/')
.slice(-2)
.map((topic) => topic.replace(/-/g, ' '));
const roadmapTitle = roadmapId.replace(/-/g, ' ');
let prompt = `I am reading a guide about "${roadmapTitle}". I am on the topic "${parentTopic}". I want to know more about "${childTopic}". Write me a brief summary for that topic. Content should be in markdown. Behave as if you are the author of the guide.`;
if (!childTopic) {
prompt = `I am reading a guide about "${roadmapTitle}". I am on the topic "${parentTopic}". I want to know more about "${parentTopic}". Write me a brief summary for that topic. Content should be in markdown. Behave as if you are the author of the guide.`;
}
console.log(`Genearting '${childTopic || parentTopic}'...`);
return new Promise((resolve, reject) => {
openai
.createChatCompletion({
model: 'gpt-4',
messages: [
{
role: 'user',
content: prompt,
},
],
})
.then((response) => {
const article = response.data.choices[0].message.content;
resolve(article);
})
.catch((err) => {
reject(err);
});
});
}
async function run() {
const topicUrlToPathMapping = getFilesInFolder(ROADMAP_CONTENT_DIR);
const roadmapJson = require(path.join(ROADMAP_JSON_DIR, `${roadmapId}.json`));
const groups = roadmapJson?.mockup?.controls?.control?.filter(
(control) =>
control.typeID === '__group__' &&
!control.properties?.controlName?.startsWith('ext_link')
);
if (!OPEN_AI_API_KEY) {
console.log('----------------------------------------');
console.log('OPEN_AI_API_KEY not found. Skipping openai api calls...');
console.log('----------------------------------------');
}
for (let group of groups) {
const topicId = group?.properties?.controlName;
const topicTitle = group?.children?.controls?.control?.find(
(control) => control?.typeID === 'Label'
)?.properties?.text;
const currTopicUrl = topicId?.replace(/^\d+-/g, '/')?.replace(/:/g, '/');
if (!currTopicUrl) {
continue;
}
const contentFilePath = topicUrlToPathMapping[currTopicUrl];
if (!contentFilePath) {
console.log(`Missing file for: ${currTopicUrl}`);
return;
}
const currentFileContent = fs.readFileSync(contentFilePath, 'utf8');
const isFileEmpty = currentFileContent.replace(/^#.+/, ``).trim() === '';
if (!isFileEmpty) {
console.log(`Ignoring ${topicId}. Not empty.`);
continue;
}
let newFileContent = `# ${topicTitle}`;
if (!OPEN_AI_API_KEY) {
console.log(`Writing ${topicId}..`);
fs.writeFileSync(contentFilePath, newFileContent, 'utf8');
continue;
}
const topicContent = await writeTopicContent(currTopicUrl);
newFileContent += `\n\n${topicContent}`;
console.log(`Writing ${topicId}..`);
fs.writeFileSync(contentFilePath, newFileContent, 'utf8');
// console.log(currentFileContent);
// console.log(currTopicUrl);
// console.log(topicTitle);
// console.log(topicUrlToPathMapping[currTopicUrl]);
}
}
run()
.then(() => {
console.log('Done');
})
.catch((err) => {
console.error(err);
process.exit(1);
});

166
bin/roadmap-dirs.cjs Normal file
View File

@@ -0,0 +1,166 @@
const fs = require('fs');
const path = require('path');
const CONTENT_DIR = path.join(__dirname, '../content');
// Directory containing the roadmaps
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../src/data/roadmaps');
const roadmapId = process.argv[2];
const allowedRoadmapIds = fs.readdirSync(ROADMAP_CONTENT_DIR);
if (!roadmapId) {
console.error('roadmapId is required');
process.exit(1);
}
if (!allowedRoadmapIds.includes(roadmapId)) {
console.error(`Invalid roadmap key ${roadmapId}`);
console.error(`Allowed keys are ${allowedRoadmapIds.join(', ')}`);
process.exit(1);
}
// Directory holding the roadmap content files
const roadmapDirName = fs
.readdirSync(ROADMAP_CONTENT_DIR)
.find((dirName) => dirName.replace(/\d+-/, '') === roadmapId);
if (!roadmapDirName) {
console.error('Roadmap directory not found');
process.exit(1);
}
const roadmapDirPath = path.join(ROADMAP_CONTENT_DIR, roadmapDirName);
const roadmapContentDirPath = path.join(
ROADMAP_CONTENT_DIR,
roadmapDirName,
'content'
);
// If roadmap content already exists do not proceed as it would override the files
if (fs.existsSync(roadmapContentDirPath)) {
console.error(`Roadmap content already exists @ ${roadmapContentDirPath}`);
process.exit(1);
}
function prepareDirTree(control, dirTree, dirSortOrders) {
// Directories are only created for groups
if (control.typeID !== '__group__') {
return;
}
// e.g. 104-testing-your-apps:other-options
const controlName = control?.properties?.controlName || '';
// e.g. 104
const sortOrder = controlName.match(/^\d+/)?.[0];
// No directory for a group without control name
if (!controlName || !sortOrder) {
return;
}
// e.g. testing-your-apps:other-options
const controlNameWithoutSortOrder = controlName.replace(/^\d+-/, '');
// e.g. ['testing-your-apps', 'other-options']
const dirParts = controlNameWithoutSortOrder.split(':');
// Nest the dir path in the dirTree
let currDirTree = dirTree;
dirParts.forEach((dirPart) => {
currDirTree[dirPart] = currDirTree[dirPart] || {};
currDirTree = currDirTree[dirPart];
});
dirSortOrders[controlNameWithoutSortOrder] = Number(sortOrder);
const childrenControls = control.children.controls.control;
// No more children
if (childrenControls.length) {
childrenControls.forEach((childControl) => {
prepareDirTree(childControl, dirTree, dirSortOrders);
});
}
return { dirTree, dirSortOrders };
}
const roadmap = require(path.join(
__dirname,
`../public/jsons/roadmaps/${roadmapId}`
));
const controls = roadmap.mockup.controls.control;
// Prepare the dir tree that we will be creating and also calculate the sort orders
const dirTree = {};
const dirSortOrders = {};
controls.forEach((control) => {
prepareDirTree(control, dirTree, dirSortOrders);
});
/**
* @param parentDir Parent directory in which directory is to be created
* @param dirTree Nested dir tree to be created
* @param sortOrders Mapping from groupName to sort order
* @param filePaths The mapping from groupName to file path
*/
function createDirTree(parentDir, dirTree, sortOrders, filePaths = {}) {
const childrenDirNames = Object.keys(dirTree);
const hasChildren = childrenDirNames.length !== 0;
// @todo write test for this, yolo for now
const groupName = parentDir
.replace(roadmapContentDirPath, '') // Remove base dir path
.replace(/(^\/)|(\/$)/g, '') // Remove trailing slashes
.replace(/(^\d+?-)/g, '') // Remove sorting information
.replaceAll('/', ':') // Replace slashes with `:`
.replace(/:\d+-/, ':');
const humanizedGroupName = groupName
.split(':')
.pop()
?.replaceAll('-', ' ')
.replace(/^\w/, ($0) => $0.toUpperCase());
const sortOrder = sortOrders[groupName] || '';
// Attach sorting information to dirname
// e.g. /roadmaps/100-frontend/content/internet
// ———> /roadmaps/100-frontend/content/103-internet
if (sortOrder) {
parentDir = parentDir.replace(/(.+?)([^\/]+)?$/, `$1${sortOrder}-$2`);
}
// If no children, create a file for this under the parent directory
if (!hasChildren) {
let fileName = `${parentDir}.md`;
fs.writeFileSync(fileName, `# ${humanizedGroupName}`);
filePaths[groupName || 'home'] = fileName.replace(CONTENT_DIR, '');
return filePaths;
}
// There *are* children, so create the parent as a directory
// and create `index.md` as the content file for this
fs.mkdirSync(parentDir);
let readmeFilePath = path.join(parentDir, 'index.md');
fs.writeFileSync(readmeFilePath, `# ${humanizedGroupName}`);
filePaths[groupName || 'home'] = readmeFilePath.replace(CONTENT_DIR, '');
// For each of the directory names, create a
// directory inside the given directory
childrenDirNames.forEach((dirName) => {
createDirTree(
path.join(parentDir, dirName),
dirTree[dirName],
dirSortOrders,
filePaths
);
});
return filePaths;
}
// Create directories and get back the paths for created directories
createDirTree(roadmapContentDirPath, dirTree, dirSortOrders);
console.log('Created roadmap content directory structure');

View File

@@ -6,7 +6,7 @@ if (!roadmapId) {
console.error('Error: roadmapId is required');
}
const fullPath = path.join(__dirname, `../src/roadmaps/${roadmapId}`);
const fullPath = path.join(__dirname, `../src/data/roadmaps/${roadmapId}`);
if (!fs.existsSync(fullPath)) {
console.error(`Error: path not found: ${fullPath}!`);
process.exit(1);

171
bin/update-sponsors.cjs Normal file
View File

@@ -0,0 +1,171 @@
const path = require('path');
const fs = require('fs');
const yaml = require('js-yaml');
const apiKey = process.env.SPONSOR_SHEET_API_KEY;
const sheetId = process.env.SPONSOR_SHEET_ID;
if (!apiKey || !sheetId) {
console.error('Missing API key or sheet ID');
process.exit(1);
}
const sheetRange = 'A3:I1001';
const sheetUrl = `https://sheets.googleapis.com/v4/spreadsheets/${sheetId}/values/${sheetRange}?key=${apiKey}`;
function removeAllSponsors(baseContentDir) {
console.log('------------------------');
console.log('Removing sponsors from: ', baseContentDir);
console.log('------------------------');
const dataDirPath = path.join(__dirname, '../src/data');
const contentDirPath = path.join(dataDirPath, baseContentDir);
const contentDir = fs.readdirSync(contentDirPath);
contentDir.forEach((content) => {
console.log('Removing sponsor from: ', content);
const pageFilePath = path.join(contentDirPath, content, `${content}.md`);
const pageFileContent = fs.readFileSync(pageFilePath, 'utf8');
const frontMatterRegex = /---\n([\s\S]*?)\n---/;
const existingFrontmatter = pageFileContent.match(frontMatterRegex)[1];
const contentWithoutFrontmatter = pageFileContent
.replace(frontMatterRegex, ``)
.trim();
let frontmatterObj = yaml.load(existingFrontmatter);
delete frontmatterObj.sponsor;
const newFrontmatter = yaml.dump(frontmatterObj, {
lineWidth: 10000,
forceQuotes: true,
quotingType: "'",
});
const newContent = `---\n${newFrontmatter}---\n${contentWithoutFrontmatter}`;
fs.writeFileSync(pageFilePath, newContent, 'utf8');
});
}
function addPageSponsor({
pageUrl,
company,
redirectUrl,
imageUrl,
adTitle,
adDescription,
}) {
const urlPart = pageUrl
.replace('https://roadmap.sh/', '')
.replace(/\?.+?$/, '');
const parentDir = urlPart.startsWith('best-practices/')
? 'best-practices'
: 'roadmaps';
const pageId = urlPart.replace(`${parentDir}/`, '');
const pageFilePath = path.join(
__dirname,
`../src/data/${parentDir}`,
`${pageId}/${pageId}.md`
);
if (!fs.existsSync(pageFilePath)) {
console.error(`Page file not found: ${pageFilePath}`);
process.exit(1);
}
console.log(`Updating page: ${urlPart}`);
const pageFileContent = fs.readFileSync(pageFilePath, 'utf8');
const frontMatterRegex = /---\n([\s\S]*?)\n---/;
const existingFrontmatter = pageFileContent.match(frontMatterRegex)[1];
const contentWithoutFrontmatter = pageFileContent
.replace(frontMatterRegex, ``)
.trim();
let frontmatterObj = yaml.load(existingFrontmatter);
delete frontmatterObj.sponsor;
const frontmatterValues = Object.entries(frontmatterObj);
const roadmapLabel = frontmatterObj.briefTitle;
// Insert sponsor data at 10 index i.e. after
// roadmap dimensions in the frontmatter
frontmatterValues.splice(10, 0, [
'sponsor',
{
url: redirectUrl,
title: adTitle,
imageUrl,
description: adDescription,
event: {
category: 'SponsorClick',
action: `${company} Redirect`,
label: `${roadmapLabel} / ${company} Link`,
},
},
]);
frontmatterObj = Object.fromEntries(frontmatterValues);
const newFrontmatter = yaml.dump(frontmatterObj, {
lineWidth: 10000,
forceQuotes: true,
quotingType: "'",
});
const newContent = `---\n${newFrontmatter}---\n\n${contentWithoutFrontmatter}`;
fs.writeFileSync(pageFilePath, newContent, 'utf8');
}
// Remove sponsors from all roadmaps
removeAllSponsors('roadmaps');
removeAllSponsors('best-practices');
console.log('------------------------');
console.log('Adding sponsors');
console.log('------------------------');
fetch(sheetUrl)
.then((res) => res.json())
.then((rawData) => {
const rows = rawData.values;
rows.map((row) => {
// prettier-ignore
const [
pageUrl,
company,
redirectUrl,
imageUrl,
adTitle,
adDescription,
startDate,
endDate,
isActive,
] = row;
const isConfiguredActive = isActive?.toLowerCase() === 'yes';
const currentDate = new Date();
const isDateInRange =
currentDate >= new Date(startDate) && currentDate <= new Date(endDate);
if (!isConfiguredActive || !isDateInRange) {
return;
}
addPageSponsor({
pageUrl,
company,
redirectUrl,
imageUrl,
adTitle,
adDescription,
startDate,
endDate,
isActive,
});
});
});

View File

@@ -14,21 +14,21 @@ appearance, race, religion, or sexual identity and orientation.
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities

View File

@@ -23,7 +23,7 @@ For the existing roadmaps, please follow the details listed for the nature of co
## Adding Content
Find [the content directory inside the relevant roadmap](https://github.com/kamranahmedse/developer-roadmap/tree/master/src/roadmaps). Please keep the following guidelines in mind when submitting content:
Find [the content directory inside the relevant roadmap](https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/roadmaps). Please keep the following guidelines in mind when submitting content:
- Content must be in English.
- Put a brief description about the topic on top of the file and the a list of links below with each link having title of the URL.

View File

@@ -8,33 +8,37 @@
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"format": "prettier --write .",
"astro": "astro",
"deploy": "NODE_DEBUG=gh-pages gh-pages -d dist -t",
"compress:jsons": "node bin/compress-jsons.cjs",
"upgrade": "ncu -u",
"roadmap-links": "node bin/roadmap-links.cjs",
"roadmap-dirs": "node bin/roadmap-dirs.cjs",
"roadmap-content": "node bin/roadmap-content.cjs",
"best-practice-content": "node bin/best-practice-content.cjs",
"best-practice-dirs": "node bin/best-practice-dirs.cjs",
"test:e2e": "playwright test"
},
"dependencies": {
"@astrojs/sitemap": "^1.0.0",
"@astrojs/tailwind": "^2.1.3",
"astro": "^1.9.2",
"astro-compress": "^1.1.28",
"node-html-parser": "^6.1.4",
"npm-check-updates": "^16.6.2",
"@astrojs/sitemap": "^1.2.1",
"@astrojs/tailwind": "^3.1.1",
"astro": "^2.1.7",
"astro-compress": "^1.1.35",
"node-html-parser": "^6.1.5",
"npm-check-updates": "^16.8.0",
"rehype-external-links": "^2.0.1",
"roadmap-renderer": "^1.0.4",
"tailwindcss": "^3.2.4"
"tailwindcss": "^3.2.7"
},
"devDependencies": {
"@playwright/test": "^1.29.2",
"@playwright/test": "^1.32.1",
"@tailwindcss/typography": "^0.5.9",
"gh-pages": "^5.0.0",
"json-to-pretty-yaml": "^1.2.2",
"js-yaml": "^4.1.0",
"markdown-it": "^13.0.1",
"prettier": "^2.8.3",
"prettier-plugin-astro": "^0.7.2"
"openai": "^3.2.1",
"prettier": "^2.8.7",
"prettier-plugin-astro": "^0.8.0",
"prettier-plugin-tailwindcss": "^0.2.6"
}
}

View File

@@ -100,7 +100,7 @@ const config: PlaywrightTestConfig = {
/* Run your local dev server before starting the tests */
webServer: {
command: 'npm run dev',
url: "http://localhost:3000",
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
};

5877
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

View File

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 KiB

BIN
public/roadmaps/mongodb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 KiB

View File

@@ -38,6 +38,7 @@ Here is the list of available roadmaps with more being actively worked upon.
- [Software Architect Roadmap](https://roadmap.sh/software-architect)
- [Software Design and Architecture Roadmap](https://roadmap.sh/software-design-architecture)
- [JavaScript Roadmap](https://roadmap.sh/javascript)
- [TypeScript Roadmap](https://roadmap.sh/typescript)
- [React Roadmap](https://roadmap.sh/react)
- [Vue Roadmap](https://roadmap.sh/vue)
- [Angular Roadmap](https://roadmap.sh/angular)
@@ -54,6 +55,16 @@ Here is the list of available roadmaps with more being actively worked upon.
- [Blockchain Roadmap](https://roadmap.sh/blockchain)
- [ASP.NET Core Roadmap](https://roadmap.sh/aspnet-core)
- [System Design Roadmap](https://roadmap.sh/system-design)
- [Kubernetes Roadmap](https://roadmap.sh/kubernetes)
- [Cyber Security Roadmap](https://roadmap.sh/cyber-security)
- [MongoDB Roadmap](https://roadmap.sh/mongodb)
- [UX Design Roadmap](https://roadmap.sh/ux-design)
We have also added a new form of visual content covering best practices:
- [Frontend Performance Best Practices](https://roadmap.sh/best-practices/frontend-performance)
- [API Security Best Practices](https://roadmap.sh/best-practices/api-security)
- [AWS Best Practices](https://roadmap.sh/best-practices/aws)
![](https://i.imgur.com/waxVImv.png)

View File

@@ -2,20 +2,21 @@ import path from 'node:path';
import fs from 'node:fs/promises';
async function getRoadmapIds() {
return fs.readdir(path.join(process.cwd(), 'src/roadmaps'));
return fs.readdir(path.join(process.cwd(), 'src/data/roadmaps'));
}
async function getBestPracticesIds() {
return fs.readdir(path.join(process.cwd(), 'src/best-practices'));
return fs.readdir(path.join(process.cwd(), 'src/data/best-practices'));
}
export function shouldIndexPage(page) {
export function shouldIndexPage(pageUrl) {
return ![
'https://roadmap.sh/404',
'https://roadmap.sh/terms',
'https://roadmap.sh/privacy',
'https://roadmap.sh/pdfs',
].includes(page);
'https://roadmap.sh/g',
].includes(pageUrl);
}
export async function serializeSitemap(item) {
@@ -26,8 +27,13 @@ export async function serializeSitemap(item) {
'https://roadmap.sh/best-practices',
'https://roadmap.sh/guides',
'https://roadmap.sh/videos',
...(await getRoadmapIds()).flatMap((id) => [`https://roadmap.sh/${id}`, `https://roadmap.sh/${id}/topics`]),
...(await getBestPracticesIds()).map((id) => `https://roadmap.sh/best-practices/${id}`),
...(await getRoadmapIds()).flatMap((id) => [
`https://roadmap.sh/${id}`,
`https://roadmap.sh/${id}/topics`,
]),
...(await getBestPracticesIds()).map(
(id) => `https://roadmap.sh/best-practices/${id}`
),
];
// Roadmaps and other high priority pages
@@ -43,7 +49,10 @@ export async function serializeSitemap(item) {
}
// Guide and video pages
if (item.url.startsWith('https://roadmap.sh/guides') || item.url.startsWith('https://roadmap.sh/videos')) {
if (
item.url.startsWith('https://roadmap.sh/guides') ||
item.url.startsWith('https://roadmap.sh/videos')
) {
return {
...item,
// @ts-ignore

View File

@@ -1,90 +0,0 @@
# Recommended Guides
> Optimize the critical rendering path:
* [Critical CSS? Not So Fast!](https://csswizardry.com/2022/09/critical-css-not-so-fast/)
* [Priority Hints - What Your Browser Doesnt Know (Yet)](https://www.etsy.com/codeascraft/priority-hints-what-your-browser-doesnt-know-yet)
* [Optimizing resource loading with Priority Hints](https://web.dev/priority-hints/)
* [Chrome Resource Priorities and Scheduling](https://docs.google.com/document/d/1bCDuq9H1ih9iNjgzyAL0gpwNFiEP4TZS-YLRp_RuMlc/edit?usp=sharing)
* [How To Optimize CSS for Peak Site Performance](https://kinsta.com/blog/optimize-css/)
* [Eliminate render blocking CSS to improve start render time](https://www.jeffreyknox.dev/blog/eliminate-render-blocking-css-to-improve-start-render-time/)
* [Small Bundles, Fast Pages: What To Do With Too Much JavaScript](https://calibreapp.com/blog/bundle-size-optimization)
* [How to Eliminate Render-Blocking Resources: a Deep Dive](https://sia.codes/posts/render-blocking-resources/)
* [The Critical Request: How to Prioritise Requests to Improve Speed](https://calibreapp.com/blog/critical-request)
* [How to Improve CSS Performance](https://calibreapp.com/blog/css-performance)
* [The Simplest Way to Load CSS Asynchronously](https://www.filamentgroup.com/lab/load-css-simpler/)
* [CSS audit](https://css-tricks.com/a-quick-css-audit-and-general-notes-about-design-systems/)
* [Measuring the Critical Rendering Path](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/measure-crp)
* [Inlining or Caching? Both Please!](https://www.filamentgroup.com/lab/inlining-cache.html)
* [CSS and Network Performance](https://csswizardry.com/2018/11/css-and-network-performance/)
* [Analyzing Critical Rendering Path Performance](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/analyzing-crp)
* [Front-End Performance Checklist](https://github.com/thedaviddias/Front-End-Performance-Checklist)
* [The PRPL Pattern](https://developers.google.com/web/fundamentals/performance/prpl-pattern/)
* [Now You See Me: How To Defer, Lazy-Load And Act With IntersectionObserver](https://www.smashingmagazine.com/2018/01/deferring-lazy-loading-intersection-observer-api/)
* [Optimising the front end for the browser](https://hackernoon.com/optimising-the-front-end-for-the-browser-f2f51a29c572)
* [Prefer DEFER Over ASYNC](https://calendar.perfplanet.com/2016/prefer-defer-over-async/)
* [A comprehensive guide to font loading strategies](https://www.zachleat.com/web/comprehensive-webfonts/)
* [Understanding the critical rendering path, rendering pages in 1 second](https://medium.com/@luisvieira_gmr/understanding-the-critical-rendering-path-rendering-pages-in-1-second-735c6e45b47a)
* [More Weight Doesnt Mean More Wait](https://www.filamentgroup.com/lab/weight-wait.html)
> JavaScript Rendering Performance
* [Five Data-Loading Patterns To Boost Web Performance](https://www.smashingmagazine.com/2022/09/data-loading-patterns-improve-frontend-performance/)
* [Optimize long tasks](https://web.dev/optimize-long-tasks/)
* [The impact of removing jQuery on our web performance](https://insidegovuk.blog.gov.uk/2022/08/15/the-impact-of-removing-jquery-on-our-web-performance/)
* [Profiling & Optimizing the runtime performance with the DevTools Performance tab](iamtk.co/profiling-and-optimizing-the-runtime-performance-with-the-devtools-performance-tab)
* [Don't fight the browser preload scanner](https://web.dev/preload-scanner/)
* [The Web Performance impact of jQuery](https://twitter.com/TheRealNooshu/status/1509487050122276864)
* [Have Single-Page Apps Ruined the Web? | Transitional Apps](https://www.youtube.com/watch?v=860d8usGC0o)
* [Improve how you architect webapps](https://www.patterns.dev/)
* [Nuxt SSR Optimizing Tips](https://vueschool.io/articles/vuejs-tutorials/nuxt-ssr-optimizing-tips/, Filip Rakowski
* [GPU accelerated JavaScript](https://gpu.rocks/#/)
* [Introducing Partytown 🎉: Run Third-Party Scripts From a Web Worker](https://dev.to/adamdbradley/introducing-partytown-run-third-party-scripts-from-a-web-worker-2cnp)
* [Astro: Astro is a fresh but familiar approach to building websites. Astro combines decades of proven performance best practices with the DX improvements of the component-oriented era. Use your favorite JavaScript framework and automatically ship the bare-minimum amount of JavaScript—by default.](https://docs.astro.build/getting-started/)
* [Minimising Layout and Layout thrashing for 60 FPS](https://www.charistheo.io/blog/2021/09/dom-reflow-and-layout-thrashing/)
* [Does shadow DOM improve style performance?](https://nolanlawson.com/2021/08/15/does-shadow-dom-improve-style-performance/)
* [Debugging memory leaks - HTTP 203](https://www.youtube.com/watch?v=YDU_3WdfkxA)
* [Explore JavaScript Dependencies With Lighthouse Treemap](https://sia.codes/posts/lighthouse-treemap/)
* [The real cost of Javascript dependencies (and the state of JS package quality)](https://medium.com/voodoo-engineering/the-real-cost-of-javascript-dependencies-and-the-state-of-js-package-quality-a8dacd74c0ec)
* [The State Of Web Workers In 2021](https://www.smashingmagazine.com/2021/06/web-workers-2021/)
* [Techniques for developing high-performance animations](https://web.dev/animations/)
* [Building a Faster Web Experience with the postTask Scheduler](https://medium.com/airbnb-engineering/building-a-faster-web-experience-with-the-posttask-scheduler-276b83454e91), Callie (Airbnb Engineering & Data Science)
* [Dont attach tooltips to document.body Learn how the browser works Debug forced reflow](https://atfzl.com/don-t-attach-tooltips-to-document-body)
* [How to Create and Fix Memory Leaks With Chrome DevTools](https://betterprogramming.pub/build-me-an-angular-app-with-memory-leaks-please-36302184e658)
* [JavaScript performance beyond bundle size](https://nolanlawson.com/2021/02/23/javascript-performance-beyond-bundle-size/)
* [The Import On Interaction Pattern](https://addyosmani.com/blog/import-on-interaction/)
* [The “Live DOM” Is Not “Slow”, “Bad”, Or “Wrong”. Web Developers Are.](https://levelup.gitconnected.com/the-live-dom-is-not-slow-bad-or-wrong-web-developers-are-2bf86c3b9e2e)
* [Prevent layout shifts with CSS grid stacks](https://www.hsablonniere.com/prevent-layout-shifts-with-css-grid-stacks--qcj5jo/)
* [content-visibility: the new CSS property that boosts your rendering performance](https://web.dev/content-visibility/)
* [Preact vs React - Updating React at Etsy](https://github.com/mq2thez/blog/blob/main/upgrade-react-etsy/preact-vs-react.md)
* [The Cost of Javascript Frameworks](https://timkadlec.com/remembers/2020-04-21-the-cost-of-javascript-frameworks/)
* [Fixing memory leaks in web applications](https://nolanlawson.com/2020/02/19/fixing-memory-leaks-in-web-applications/)
* [How to load polyfills only when needed](https://3perf.com/blog/polyfills/)
* [Responsible JavaScript: Part III - Third parties](https://alistapart.com/article/responsible-javascript-part-3/)
* [The cost of JavaScript in 2019](https://v8.dev/blog/cost-of-javascript-2019)
* [When should you be using Web Workers?](https://dassur.ma/things/when-workers/)
* [Responsible Javascript: Part II - Code Bundle](https://alistapart.com/article/responsible-javascript-part-2/)
* [Faster script loading with BinaryAST?](https://blog.cloudflare.com/binary-ast/)
* [Svelte 3: Rethinking reactivity](https://svelte.dev/blog/svelte-3-rethinking-reactivity)
* [Responsible Javascript: Part I - Web platform over frameworks](https://alistapart.com/article/responsible-javascript-part-1/)
* [JavaScript Loading Priorities in Chrome](https://addyosmani.com/blog/script-priorities/)
* [Idle Until Urgent](https://philipwalton.com/articles/idle-until-urgent/)
* [Browser painting and considerations for web performance](https://css-tricks.com/browser-painting-and-considerations-for-web-performance/)
* [The Cost Of JavaScript In 2018](https://medium.com/@addyosmani/the-cost-of-javascript-in-2018-7d8950fbb5d4) ([Video](https://www.youtube.com/watch?v=i5R7giitymk))
* [Examining Web Worker Performance](https://www.loxodrome.io/post/web-worker-performance/)
* [Front-End Performance Checklist](https://github.com/thedaviddias/Front-End-Performance-Checklist)
* [jankfree](http://jankfree.org/)
* [What forces layout/reflow?](https://gist.github.com/paulirish/5d52fb081b3570c81e3a)
* [Using requestIdleCallback](https://developers.google.com/web/updates/2015/08/using-requestidlecallback)
* [Optimize Javascript Execution](https://developers.google.com/web/fundamentals/performance/rendering/optimize-javascript-execution)
* [Why Web Developers Need to Care about Interactivity](https://philipwalton.com/articles/why-web-developers-need-to-care-about-interactivity/)
* [Improving Performance with the Paint Timing API](https://www.sitepen.com/blog/2017/10/06/improving-performance-with-the-paint-timing-api)
* [Deploying ES2015+ Code in Production Today](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/)
* [Performant Web Animations and Interactions: Achieving 60 FPS](https://blog.algolia.com/performant-web-animations/)
* [JavaScript Start-up Performance](https://medium.com/reloading/javascript-start-up-performance-69200f43b201)
* [Performant Parallaxing](https://developers.google.com/web/updates/2016/12/performant-parallaxing)
* [The Anatomy of a Frame](https://aerotwist.com/blog/the-anatomy-of-a-frame/)
* [The future of loading CSS](https://jakearchibald.com/2016/link-in-body/)
* [4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them](https://auth0.com/blog/four-types-of-leaks-in-your-javascript-code-and-how-to-get-rid-of-them/)
* [The cost of frameworks](https://aerotwist.com/blog/the-cost-of-frameworks/)
* [FLIP Your Animations](https://aerotwist.com/blog/flip-your-animations/)

View File

@@ -1,6 +0,0 @@
# Use CDN
Use a CDN to serve your static assets. This will reduce the load on your server and improve the performance of your site.
- [10 Tips to Optimize CDN Performance - CDN Planet](https://www.cdnplanet.com/blog/10-tips-optimize-cdn-performance/)
- [HTTP Caching | Web Fundamentals | Google Developers](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching)

View File

@@ -1,29 +0,0 @@
---
jsonUrl: "/jsons/best-practices/frontend-performance.json"
pdfUrl: "/pdfs/best-practices/frontend-performance.pdf"
order: 1
featuredTitle: "Frontend Performance"
featuredDescription: "Frontend Performance Best Practices"
isNew: true
isUpcoming: false
title: "Frontend Performance"
description: "Detailed list of best practices to improve your frontend performance"
dimensions:
width: 968
height: 1270.89
schema:
headline: "Frontend Performance Best Practices"
description: "Detailed list of best practices to improve the frontend performance of your website. Each best practice carries further details and how to implement that best practice."
imageUrl: "https://roadmap.sh/best-practices/frontend-performance.png"
datePublished: "2023-01-23"
dateModified: "2023-01-23"
seo:
title: "Frontend Performance Best Practices"
description: "Detailed list of best practices to improve the frontend performance of your website. Each best practice carries further details and how to implement that best practice."
keywords:
- "frontend performance"
- "frontend performance best practices"
- "frontend performance checklist"
- "frontend checklist"
- "make performant frontends"
---

View File

@@ -2,6 +2,8 @@ export {};
declare global {
interface Window {
// To selectively enable/disable debug logs
__DEBUG__: boolean;
gtag: any;
fireEvent: (props: GAEventType) => void;
}
@@ -27,6 +29,10 @@ window.fireEvent = (props: GAEventType) => {
return;
}
if (window.__DEBUG__) {
console.log('Analytics event fired', props);
}
window.gtag('event', action, {
event_category: category,
event_label: label,

View File

@@ -3,18 +3,16 @@ import Popup from './Popup/Popup.astro';
import CaptchaFields from './Captcha/CaptchaFields.astro';
---
<Popup
id='download-popup'
title='Download'
subtitle='Enter your email below to receive the download link.'
>
<Popup id='download-popup' title='Download' subtitle='Enter your email below to receive the download link.'>
<form
action='https://newsletter.roadmap.sh/subscribe'
action='https://news.roadmap.sh/subscribe'
method='POST'
accept-charset='utf-8'
target='_blank'
captcha-form
>
<input type='hidden' name='gdpr' value='true' />
<input
type='email'
name='email'
@@ -42,13 +40,11 @@ import CaptchaFields from './Captcha/CaptchaFields.astro';
</Popup>
<script>
document
.querySelector('[submit-download-form]')
?.addEventListener('click', () => {
window.fireEvent({
category: 'Subscription',
action: 'Submitted Popup Form',
label: 'Download Roadmap Popup',
});
document.querySelector('[submit-download-form]')?.addEventListener('click', () => {
window.fireEvent({
category: 'Subscription',
action: 'Submitted Popup Form',
label: 'Download Roadmap Popup',
});
});
</script>

View File

@@ -19,13 +19,13 @@ if (faqs.length === 0) {
}
---
<div class='border-t bg-gray-100'>
<div class='border-t bg-gray-100 mt-8'>
<div class='container'>
<div class='flex justify-between relative -top-5'>
<h1 class='text-sm sm:text-base font-medium py-1 px-3 border bg-white rounded-md'>Frequently Asked Questions</h1>
</div>
<div class='flex flex-col gap-1 pb-8'>
<div class='flex flex-col gap-1 pb-14'>
{
faqs.map((faq, questionIndex) => (
<Question isActive={questionIndex === 0} question={faq.question}>

View File

@@ -1,6 +1,5 @@
---
import Loader from '../Loader.astro';
import TopicOverlay from '../TopicOverlay/TopicOverlay.astro';
import './FrameRenderer.css';
export interface Props {
@@ -17,13 +16,15 @@ const { resourceId, resourceType, jsonUrl, dimensions = null } = Astro.props;
---
<div
id='resource-svg'
id='resource-svg-wrap'
style={dimensions ? `--aspect-ratio:${dimensions.width}/${dimensions.height}` : null}
data-resource-type={resourceType}
data-resource-id={resourceId}
data-json-url={jsonUrl}
>
<Loader />
<div id='resource-loader'>
<Loader />
</div>
</div>
<script src='./renderer.js'></script>

View File

@@ -5,14 +5,21 @@ export class Renderer {
this.resourceId = '';
this.resourceType = '';
this.jsonUrl = '';
this.loaderHTML = null;
this.containerId = 'resource-svg';
this.containerId = 'resource-svg-wrap';
this.loaderId = 'resource-loader';
this.init = this.init.bind(this);
this.onDOMLoaded = this.onDOMLoaded.bind(this);
this.jsonToSvg = this.jsonToSvg.bind(this);
this.handleSvgClick = this.handleSvgClick.bind(this);
this.prepareConfig = this.prepareConfig.bind(this);
this.switchRoadmap = this.switchRoadmap.bind(this);
}
get loaderEl() {
return document.getElementById(this.loaderId);
}
get containerEl() {
@@ -24,6 +31,8 @@ export class Renderer {
return false;
}
// Clone it so we can use it later
this.loaderHTML = this.loaderEl.innerHTML;
const dataset = this.containerEl.dataset;
this.resourceType = dataset.resourceType;
@@ -43,14 +52,30 @@ export class Renderer {
return null;
}
this.containerEl.innerHTML = this.loaderHTML;
return fetch(jsonUrl)
.then(function (res) {
.then((res) => {
return res.json();
})
.then(function (json) {
.then((json) => {
return wireframeJSONToSVG(json, {
fontURL: '/fonts/balsamiq.woff2',
});
})
.then((svg) => {
this.containerEl.replaceChildren(svg);
})
.catch((error) => {
const message = `
<strong>There was an error.</strong><br>
Try loading the page again. or submit an issue on GitHub with following:<br><br>
${error.message} <br /> ${error.stack}
`;
this.containerEl.innerHTML = `<div class="error py-5 text-center text-red-600 mx-auto">${message}</div>`;
});
}
@@ -59,11 +84,44 @@ export class Renderer {
return;
}
this.jsonToSvg(this.jsonUrl)
.then((svg) => {
document.getElementById(this.containerId).replaceChildren(svg);
})
.catch(console.error);
const urlParams = new URLSearchParams(window.location.search);
const roadmapType = urlParams.get('r');
if (roadmapType) {
this.switchRoadmap(`/jsons/roadmaps/${roadmapType}.json`);
} else {
this.jsonToSvg(this.jsonUrl);
}
}
switchRoadmap(newJsonUrl) {
const newJsonFileSlug = newJsonUrl.split('/').pop().replace('.json', '');
// Update the URL and attach the new roadmap type
if (window?.history?.pushState) {
const url = new URL(window.location);
const type = this.resourceType[0]; // r for roadmap, b for best-practices
url.searchParams.delete(type);
url.searchParams.set(type, newJsonFileSlug);
window.history.pushState(null, '', url.toString());
}
const pageType = this.resourceType.replace(/\b\w/g, (l) => l.toUpperCase());
window.fireEvent({
// RoadmapClick, BestPracticesClick, etc
category: `${pageType.replace('-', '')}Click`,
// roadmap/frontend/switch-version
action: `${this.resourceId}/switch-version`,
// roadmap/frontend/switch-version
label: `${newJsonFileSlug}`,
});
this.jsonToSvg(newJsonUrl).then(() => {
this.containerEl.setAttribute('style', '');
});
}
handleSvgClick(e) {
@@ -80,6 +138,14 @@ export class Renderer {
return;
}
if (/^json:/.test(groupId)) {
// e.g. /roadmaps/frontend-beginner.json
const newJsonUrl = groupId.replace('json:', '');
this.switchRoadmap(newJsonUrl);
return;
}
if (/^check:/.test(groupId)) {
window.dispatchEvent(
new CustomEvent(`${this.resourceType}.topic.toggle`, {

View File

@@ -32,14 +32,14 @@ const { author } = frontmatter;
<span class='mx-1.5'>&middot;</span>
<a
class='text-blue-400 hover:text-blue-500 hover:underline'
href={`https://github.com/kamranahmedse/roadmap.sh/tree/master/src/guides/${guide.id}.md`}
href={`https://github.com/kamranahmedse/roadmap.sh/tree/master/src/data/guides/${guide.id}.md`}
target='_blank'>Improve this Guide</a
>
</p>
<h1 class='text-2xl sm:text-5xl my-0 sm:my-3.5 font-bold'>
{frontmatter.title}
</h1>
<p class='hidden sm:block text-gray-400 text-md'>
<p class='hidden sm:block text-gray-400 text-xl'>
{frontmatter.description}
</p>
</div>

View File

@@ -1,5 +1,5 @@
<div
class='prose-blockquote:font-normal prose container prose-code:bg-transparent prose-h2:text-3xl prose-h2:mt-4 prose-h2:mb-2 prose-h3:mt-2 prose-img:mt-1'
class='prose-xl prose-blockquote:font-normal prose container prose-code:bg-transparent prose-h2:text-3xl prose-h2:mt-4 prose-h2:mb-2 prose-h3:mt-2 prose-img:mt-1'
>
<slot />
</div>

View File

@@ -7,9 +7,9 @@ const starCount = await getFormattedStars('kamranahmedse/developer-roadmap');
<div class='py-6 sm:py-16 border-b border-t text-left sm:text-center bg-white'>
<div class='max-w-[600px] container'>
<h2 class='text-2xl sm:text-5xl font-bold'>Open Source</h2>
<h2 class='text-2xl sm:text-5xl font-bold'>Community</h2>
<p class='text-gray-600 text-sm sm:text-lg leading-relaxed my-2.5 sm:my-5'>
The project is OpenSource, <a
roadmap.sh is the <a
href='https://github.com/search?o=desc&q=stars%3A%3E100000&s=stars&type=Repositories'
target='_blank'
class='font-medium text-gray-600 hover:text-black underline underline-offset-2'
@@ -17,23 +17,25 @@ const starCount = await getFormattedStars('kamranahmedse/developer-roadmap');
> and is visited by hundreds of thousands of developers every month.
</p>
<div class='block mb-1.5 sm:mb-0'>
<div class='flex justify-start flex-col sm:flex-row sm:justify-center gap-2 sm:gap-3 mb-1.5 sm:mb-0'>
<a
href='https://github.com/kamranahmedse/developer-roadmap'
target='_blank'
class='inline-flex items-center group rounded-md relative'
class='inline-flex items-center border border-black py-1.5 px-3 rounded-lg text-sm hover:text-white hover:bg-black bg-white'
>
<span
class='inline-flex items-center border border-black py-1.5 px-3 rounded-lg text-sm group-hover:text-white group-hover:bg-black relative bg-white'
>
<div class='mr-1 -ml-1'>
<Icon icon='star' />
</div>
<Icon icon='star' class='mr-1 -ml-1 fill-current' />
<span class='lowercase'>{starCount}</span>
<span class='ml-1.5 group-hover:hidden'>GitHub Stars</span>
<span class='ml-2 hidden group-hover:block'>Star us on GitHub</span>
</span>
<span class='ml-2 hover:block'>GitHub Stars</span>
</a>
<a
href='https://discord.gg/cJpEt5Qbwa'
target='_blank'
class='relative pointer inline-flex items-center border border-black py-1.5 px-3 rounded-lg text-sm hover:text-white hover:bg-black bg-white group'
>
<Icon icon='discord' class='h-[14px] mr-2 -ml-1 fill-current' />
Join on Discord <span class="rounded-sm ml-0.5 px-1.5 py-0.5 text-xs uppercase">/ New</span>
</a>
</div>
</div>

View File

@@ -0,0 +1,42 @@
---
import { getRoadmapsByIds, RoadmapFrontmatter } from '../lib/roadmap';
export interface Props {
roadmap: RoadmapFrontmatter;
}
const { roadmap } = Astro.props;
const relatedRoadmaps = roadmap.relatedRoadmaps || [];
if (!relatedRoadmaps.length) {
return null;
}
const relatedRoadmapDetails = await getRoadmapsByIds(relatedRoadmaps);
---
<div class='border-t bg-gray-100'>
<div class='container'>
<div class='flex justify-between relative -top-5'>
<h1 class='text-md font-medium py-1 px-3 border bg-white rounded-md'>Related Roadmaps</h1>
<a href='/roadmaps' class='text-md font-medium py-1 px-3 border bg-white rounded-md hover:bg-gray-50'>
<span class='hidden sm:inline'>All Roadmaps &rarr;</span>
<span class='inline sm:hidden'>More &rarr;</span>
</a>
</div>
<div class='flex flex-col gap-1 pb-8'>
{
relatedRoadmapDetails.map((relatedRoadmap) => (
<a
href={`/${relatedRoadmap.id}`}
class='py-2 px-3.5 bg-white border rounded-md hover:bg-gray-50 flex flex-col sm:flex-row gap-0.5 sm:gap-0'
>
<span class='font-medium inline-block min-w-[150px]'>{relatedRoadmap.frontmatter.briefTitle}</span>
<span class='text-gray-500'>{relatedRoadmap.frontmatter.briefDescription}</span>
</a>
))
}
</div>
</div>
</div>

View File

@@ -17,6 +17,6 @@ const { roadmap, roadmapId } = Astro.props;
<span class='hidden sm:inline'>Click to visit the interactive version of</span>
<span class='inline sm:hidden'>Visit complete</span>
<span class='sm:lowercase ml-0.5 font-medium underline underline-offset-1'>{roadmap.featuredTitle} roadmap</span>
<span class='sm:lowercase ml-0.5 font-medium underline underline-offset-1'>{roadmap.briefTitle} roadmap</span>
</span>
</a>

View File

@@ -11,13 +11,14 @@ export interface Props {
title: string;
description: string;
note?: string;
tnsBannerLink?: string;
roadmapId: string;
isUpcoming?: boolean;
hasSearch?: boolean;
hasTopics?: boolean;
}
const { title, description, roadmapId, isUpcoming = false, hasSearch = false, note, hasTopics = false } = Astro.props;
const { title, description, roadmapId, tnsBannerLink, isUpcoming = false, hasSearch = false, note, hasTopics = false } = Astro.props;
const isRoadmapReady = !isUpcoming;
---
@@ -109,7 +110,7 @@ const isRoadmapReady = !isUpcoming;
</div>
<!-- Desktop: Roadmap Resources - Alert -->
{hasTopics && <RoadmapHint roadmapId={roadmapId} />}
{hasTopics && <RoadmapHint roadmapId={roadmapId} tnsBannerLink={tnsBannerLink} />}
{hasSearch && <TopicSearch />}
</div>

View File

@@ -3,11 +3,12 @@ import Icon from './Icon.astro';
export interface Props {
roadmapId: string;
tnsBannerLink?: string;
}
const { roadmapId } = Astro.props;
const { roadmapId, tnsBannerLink = '' } = Astro.props;
const hasTNSBanner = ['frontend', 'backend', 'devops'].includes(roadmapId);
const hasTNSBanner = !!tnsBannerLink;
const roadmapTitle = roadmapId === 'devops' ? 'DevOps' : `${roadmapId.charAt(0).toUpperCase()}${roadmapId.slice(1)}`;
---
@@ -26,7 +27,7 @@ const roadmapTitle = roadmapId === 'devops' ? 'DevOps' : `${roadmapId.charAt(0).
<p class='text-sm'>
Get the latest {roadmapTitle} news from our sister site{' '}
<a
href='https://thenewstack.io?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Alert'
href={tnsBannerLink}
target='_blank'
class='font-semibold underline'
ga-category='PartnerClick'

View File

@@ -25,11 +25,11 @@ const {
href={url}
id='sponsor-ad'
target='_blank'
rel='noopener sponsored'
rel='noopener sponsored nofollow'
ga-category={event?.category}
ga-action={event?.action}
ga-label={event?.label}
class='fixed bottom-[15px] right-[20px] outline-transparent z-50 bg-white max-w-[330px] shadow-lg outline-0 hidden'
class='fixed bottom-[15px] right-[15px] outline-transparent z-50 bg-white max-w-[350px] shadow-lg outline-0 hidden'
>
<button
class='absolute top-1.5 right-1.5 text-gray-300 hover:text-gray-800'

View File

@@ -3,18 +3,16 @@ import Popup from './Popup/Popup.astro';
import CaptchaFields from './Captcha/CaptchaFields.astro';
---
<Popup
id='subscribe-popup'
title='Subscribe'
subtitle='Enter your email below to receive updates.'
>
<Popup id='subscribe-popup' title='Subscribe' subtitle='Enter your email below to receive updates.'>
<form
action='https://newsletter.roadmap.sh/subscribe'
action='https://news.roadmap.sh/subscribe'
method='POST'
accept-charset='utf-8'
target='_blank'
captcha-form
>
<input type='hidden' name='gdpr' value='true' />
<input
type='email'
name='email'

View File

@@ -22,6 +22,9 @@ const { contentContributionLink } = Astro.props;
<div id='topic-actions' class='hidden mb-2'>
<button
id='mark-topic-done'
ga-category='TopicClick'
ga-action='topic/mark-completion'
ga-label='done'
class='bg-green-600 text-white p-1 px-2 text-sm rounded-md hover:bg-green-700 inline-flex items-center'
>
<Icon icon='check' />
@@ -30,6 +33,9 @@ const { contentContributionLink } = Astro.props;
<button
id='mark-topic-pending'
ga-category='TopicClick'
ga-action='topic/mark-completion'
ga-label='pending'
class='hidden bg-red-600 text-white p-1 px-2 text-sm rounded-md hover:bg-red-700 inline-flex items-center'
>
<Icon icon='reset' />

View File

@@ -15,9 +15,12 @@ export class Topic {
this.activeTopicId = null;
this.handleRoadmapTopicClick = this.handleRoadmapTopicClick.bind(this);
this.handleBestPracticeTopicClick = this.handleBestPracticeTopicClick.bind(this);
this.handleBestPracticeTopicToggle = this.handleBestPracticeTopicToggle.bind(this);
this.handleBestPracticeTopicPending = this.handleBestPracticeTopicPending.bind(this);
this.handleBestPracticeTopicClick =
this.handleBestPracticeTopicClick.bind(this);
this.handleBestPracticeTopicToggle =
this.handleBestPracticeTopicToggle.bind(this);
this.handleBestPracticeTopicPending =
this.handleBestPracticeTopicPending.bind(this);
this.close = this.close.bind(this);
this.resetDOM = this.resetDOM.bind(this);
@@ -177,7 +180,13 @@ export class Topic {
this.activeTopicId = topicId;
this.resetDOM();
this.renderTopicFromUrl(`/best-practices/${bestPracticeId}/${topicId.replaceAll(':', '/')}`);
const topicUrl = `/best-practices/${bestPracticeId}/${topicId.replaceAll(
':',
'/'
)}`;
this.renderTopicFromUrl(topicUrl).then(() => null);
}
handleRoadmapTopicClick(e) {
@@ -192,31 +201,45 @@ export class Topic {
this.activeTopicId = topicId;
this.resetDOM();
this.renderTopicFromUrl(`/${roadmapId}/${topicId.replaceAll(':', '/')}`);
const topicUrl = `/${roadmapId}/${topicId.replaceAll(':', '/')}`;
window.fireEvent({
category: `RoadmapClick`,
action: `${roadmapId}/load-topic`,
label: topicUrl,
});
this.renderTopicFromUrl(topicUrl).then(() => null);
}
querySvgElementsByTopicId(topicId) {
const matchingElements = [];
// Elements having sort order in the beginning of the group id
document.querySelectorAll(`[data-group-id$="-${topicId}"]`).forEach((element) => {
const foundGroupId = element?.dataset?.groupId || '';
const validGroupRegex = new RegExp(`^\\d+-${topicId}$`);
document
.querySelectorAll(`[data-group-id$="-${topicId}"]`)
.forEach((element) => {
const foundGroupId = element?.dataset?.groupId || '';
const validGroupRegex = new RegExp(`^\\d+-${topicId}$`);
if (validGroupRegex.test(foundGroupId)) {
matchingElements.push(element);
}
});
if (validGroupRegex.test(foundGroupId)) {
matchingElements.push(element);
}
});
// Elements with exact match of the topic id
document.querySelectorAll(`[data-group-id="${topicId}"]`).forEach((element) => {
matchingElements.push(element);
});
document
.querySelectorAll(`[data-group-id="${topicId}"]`)
.forEach((element) => {
matchingElements.push(element);
});
// Matching "check:XXXX" box of the topic
document.querySelectorAll(`[data-group-id="check:${topicId}"]`).forEach((element) => {
matchingElements.push(element);
});
document
.querySelectorAll(`[data-group-id="check:${topicId}"]`)
.forEach((element) => {
matchingElements.push(element);
});
return matchingElements;
}
@@ -247,29 +270,44 @@ export class Topic {
return;
}
const isClickedDone = e.target.id === this.markTopicDoneId || e.target.closest(`#${this.markTopicDoneId}`);
const isClickedDone =
e.target.id === this.markTopicDoneId ||
e.target.closest(`#${this.markTopicDoneId}`);
if (isClickedDone) {
this.markAsDone(this.activeTopicId);
this.close();
}
const isClickedPending = e.target.id === this.markTopicPendingId || e.target.closest(`#${this.markTopicPendingId}`);
const isClickedPending =
e.target.id === this.markTopicPendingId ||
e.target.closest(`#${this.markTopicPendingId}`);
if (isClickedPending) {
this.markAsPending(this.activeTopicId);
this.close();
}
const isClickedClose = e.target.id === this.closeTopicId || e.target.closest(`#${this.closeTopicId}`);
const isClickedClose =
e.target.id === this.closeTopicId ||
e.target.closest(`#${this.closeTopicId}`);
if (isClickedClose) {
this.close();
}
}
init() {
window.addEventListener('best-practice.topic.click', this.handleBestPracticeTopicClick);
window.addEventListener('best-practice.topic.toggle', this.handleBestPracticeTopicToggle);
window.addEventListener(
'best-practice.topic.click',
this.handleBestPracticeTopicClick
);
window.addEventListener(
'best-practice.topic.toggle',
this.handleBestPracticeTopicToggle
);
window.addEventListener('roadmap.topic.click', this.handleRoadmapTopicClick);
window.addEventListener(
'roadmap.topic.click',
this.handleRoadmapTopicClick
);
window.addEventListener('click', this.handleOverlayClick);
window.addEventListener('contextmenu', this.rightClickListener);

View File

@@ -3,13 +3,13 @@ import CaptchaFields from './Captcha/CaptchaFields.astro';
import Icon from './Icon.astro';
---
<div class='my-0 px-5 rounded-lg text-left sm:text-center'>
<div class='my-0 px-5 rounded-lg text-left sm:text-center sm:pb-10 pb-8'>
<div class='sm:max-w-[400px] mx-auto'>
<div class='hidden sm:block'><Icon icon='bell' /></div>
<h2 class='text-3xl mb-1 font-medium hidden sm:block'>Upcoming</h2>
<p class='text-gray-600 mb-0 sm:mb-5'>Please check back later or subscribe below.</p>
<form action='https://newsletter.roadmap.sh/subscribe' method='post' accept-charset='utf-8' captcha-form>
<form action='https://news.roadmap.sh/subscribe' method='post' accept-charset='utf-8' captcha-form>
<input
type='email'
required

View File

@@ -2,7 +2,8 @@
import Icon from './Icon.astro';
---
<div class='sticky top-0 border-b border-b-yellow-300 z-20 flex h-[37px]' youtube-banner>
<!-- sticky top-0 -->
<div class='border-b border-b-yellow-300 z-20 flex h-[37px]' youtube-banner>
<a
href='https://youtube.com/theroadmap?sub_confirmation=1'
target='_blank'

View File

@@ -0,0 +1,37 @@
---
jsonUrl: '/jsons/best-practices/api-security.json'
pdfUrl: '/pdfs/best-practices/api-security.pdf'
order: 2
briefTitle: 'API Security'
briefDescription: 'API Security Best Practices'
isNew: true
isUpcoming: false
title: 'API Security Best Practices'
description: 'Detailed list of best practices to make your APIs secure'
dimensions:
width: 968
height: 1543.39
sponsor:
url: 'https://liblab.com/blog/a-big-look-at-security-in-openapi?utm_source=roadmap_apisecruity&utm_medium=edge_stack&utm_campaign=april23'
title: 'Secure APIs in OpenAPI'
imageUrl: 'https://i.imgur.com/ZmuZUmS.png'
description: 'Explore OpenAPI security options, industry best practices, and steps to secure your own API.'
event:
category: 'SponsorClick'
action: 'Liblab Redirect'
label: 'API Security / Liblab Link'
schema:
headline: 'API Security Best Practices'
description: 'Detailed list of best practices to make your APIs secure. Each best practice carries further details and how to implement that best practice.'
imageUrl: 'https://roadmap.sh/best-practices/api-security.png'
datePublished: '2023-02-21'
dateModified: '2023-02-21'
seo:
title: 'API Security Best Practices'
description: 'Detailed list of best practices to make your APIs secure. Each best practice carries further details and how to implement that best practice.'
keywords:
- 'API Security'
- 'API Security Best Practices'
- 'API Security Checklist'
---

View File

@@ -0,0 +1,15 @@
# API Gateway
> Use an API Gateway for caching, Rate Limit policies, and other security features.
An API gateway can make your APIs more secure by providing a centralized point of control for managing and securing API traffic. Here are a few ways an API gateway can improve API security:
- Authentication and authorization: API gateways can handle user authentication and authorization, reducing the burden on individual APIs and improving consistency across the organization. This can include techniques such as JWT verification, OAuth, and other authentication mechanisms.
- Traffic filtering and rate limiting: An API gateway can enforce traffic filtering and rate limiting to protect APIs against DDoS attacks, brute force attacks, and other types of abuse.
- Encryption and decryption: An API gateway can handle encryption and decryption of sensitive data to protect against data breaches and theft.
- Logging and monitoring: An API gateway can provide centralized logging and monitoring of API traffic, helping to identify and respond to security threats and other issues.
- Integration with security tools: An API gateway can be integrated with security tools such as WAFs, SIEMs, and other security tools to provide additional layers of protection.

View File

@@ -0,0 +1,15 @@
# Authentication Mechanisms
> Use standard authentication mechanisms for generating tokens, storing credentials, and authenticating users.
Here are some examples of established authentication mechanisms that you can use instead of reinventing the wheel:
- OAuth: OAuth is a widely used open standard for authorization that enables users to grant third-party applications access to their resources without sharing their credentials. It is commonly used by web services and APIs to enable users to sign in with their social media accounts or other third-party accounts.
- OpenID Connect: OpenID Connect is an authentication protocol built on top of OAuth 2.0 that enables users to authenticate with multiple websites and applications using a single set of credentials. It is commonly used for single sign-on (SSO) across multiple websites and applications.
- SAML: Security Assertion Markup Language (SAML) is an XML-based standard for exchanging authentication and authorization data between parties. It is commonly used for SSO across multiple domains or organizations.
- Password hashing algorithms: Password hashing algorithms like bcrypt and scrypt are widely used to securely store and protect user passwords. These algorithms ensure that even if an attacker gains access to the password database, they will not be able to easily recover the passwords.
- Two-factor authentication (2FA): 2FA is an authentication mechanism that requires users to provide two forms of identification to access their accounts. This typically involves something the user knows (like a password) and something the user has (like a mobile device or security key). Many services and applications now offer 2FA as an additional security measure.

View File

@@ -0,0 +1,9 @@
# Authorization Header
> Use standard `Authorization` header for sending tokens instead of custom headers or query/body parameters
Sending tokens in the query or body parameters is generally not recommended because these parameters may be logged or cached by various systems, including web servers, proxies, and gateways. This can potentially lead to the exposure of sensitive data, including authentication tokens.
Additionally, sending tokens in query or body parameters can make them more vulnerable to cross-site request forgery (CSRF) attacks. In a CSRF attack, an attacker can trick a user into submitting a request that includes their authentication token, which the attacker can then use to impersonate the user and gain access to their account.
By contrast, using the `Authorization` header to send tokens helps to ensure that the tokens are not logged or cached by intermediary systems, and it can also help to protect against CSRF attacks by allowing the server to validate the token before processing the request.

View File

@@ -0,0 +1,5 @@
# Avoid HTTP Blocking
> Avoid HTTP blocking if you are using huge amount of data by moving the HTTP heavy operations to background jobs or asynchronous tasks.
HTTP blocking is a common issue in web applications. It occurs when the application is unable to process incoming HTTP requests due to a large number of requests or a large amount of data. This can lead to the application becoming unresponsive and the server crashing. This can be prevented by moving HTTP heavy operations to background jobs or asynchronous tasks. You can use a message queue to queue the requests and process them in the background. This will allow the application to continue processing other requests while the heavy operations are being processed in the background.

View File

@@ -0,0 +1,5 @@
# Avoid Logging Sensitive Data
> Ensure that you aren't logging any sensitive data.
Make sure that you are not logging any sensitive data such as passwords, credit card numbers, or personal information. This is because logging sensitive data can expose it to attackers, allowing them to gain unauthorized access to your system or data. Additionally, logging sensitive data can violate data privacy laws and regulations, exposing you to legal liability.

View File

@@ -0,0 +1,5 @@
# Avoid Personal ID in URLs
> Avoid users personal ID in the resource URLs e.g. users/242/orders
User's own resource ID should be avoided. Use `/me/orders` instead of `/user/654321/orders`. This will help avoid the risk of exposing the users personal ID that can be used for further attacks.

View File

@@ -0,0 +1,5 @@
# Avoid Returning Sensitive Data
> Only return the data that is needed for the client to function.
Returning only the data that is needed for the client to function is an important best practice for API security. This is because limiting the amount of data that is returned reduces the amount of sensitive information that is exposed. By only returning the necessary data, you can help prevent security vulnerabilities such as data leakage, injection attacks, and other types of attacks that rely on exposing too much information. Additionally, reducing the amount of data returned can improve the performance of your API by reducing the amount of data that needs to be processed and transmitted.

View File

@@ -0,0 +1,5 @@
# Use CDN for Uploads
> Use CDN for file uploads
Using a Content Delivery Network (CDN) for file uploads can make an API more secure by offloading the file upload traffic from the API server and reducing the risk of DDoS attacks.

View File

@@ -0,0 +1,13 @@
# Centralized Logins
> Use centralized logins for all services and components.
Using centralized logins for all services and components is important for several reasons:
- Centralized logins enable you to manage authentication and authorization in one place, reducing the risk of security gaps or inconsistencies across different services.
- Centralized logins provide a single point of entry, allowing you to control access and monitor activity more easily.
- Centralized logins make it easier to enforce security policies across different services and components, ensuring that only authorized users can access sensitive data or perform certain actions.
To use centralized logins, you need to set up a single sign-on (SSO) system that enables users to authenticate once and then access multiple services without having to provide credentials again. This can be done using protocols like OAuth or SAML, which enable secure authentication and authorization across different applications and services. Once set up, you can use centralized logging tools like ELK stack, Splunk, or Graylog to collect logs from different services and components and analyze them in one place. This enables you to quickly identify and respond to security threats or anomalies.

View File

@@ -0,0 +1,7 @@
# Dependencies
> Check your dependencies for known vulnerabilities and keep them up to date.
Vulnerabilities in third-party libraries and components can be exploited by attackers to gain access to your system or data. These vulnerabilities can be introduced through outdated or insecure dependencies that have not been updated with the latest security patches.
By regularly checking for vulnerabilities and keeping your dependencies up to date, you can ensure that your API is not susceptible to known security risks. This can be done by using automated tools or services that scan your codebase and provide reports on any vulnerabilities found in your dependencies. By addressing these vulnerabilities promptly, you can reduce the risk of your API being compromised by attackers.

View File

@@ -0,0 +1,5 @@
# Code Review Process
> Use a code review process and disregard self-approval.
Having a good code review process allows for additional sets of eyes to review the code and identify potential security issues or vulnerabilities. A code review process involves other team members reviewing the code to ensure it follows best practices and is secure. Disregarding self-approval means that the developer who wrote the code should not be the only one responsible for approving it for release. This helps to catch potential mistakes or oversights before the code is deployed, reducing the risk of security breaches or other issues.

View File

@@ -0,0 +1,5 @@
# Content Security Policy
> Send `Content-Security-Policy: default-src 'none'` header.
Sending the `Content-Security-Policy: default-src 'none'` header is a security best practice that helps prevent cross-site scripting (XSS) attacks. This header tells the browser to not allow any resources to be loaded from external sources, such as scripts, stylesheets, or images. It only allows resources that are explicitly whitelisted in the CSP header, such as scripts or stylesheets hosted on your own domain. This can help prevent malicious actors from injecting code into your web pages via XSS attacks, as the browser will not execute any scripts or load any resources that are not explicitly allowed by the CSP policy.

View File

@@ -0,0 +1,5 @@
# Turn Debug Mode Off
> Make sure to turn the debug mode off in production
Debug mode is a feature that is used to help developers debug their code. It is not meant to be used in production. It can expose sensitive information about the application and the server it is running on. Make sure to turn debug mode off in production.

View File

@@ -0,0 +1,7 @@
# Directory Listings
> Turn off directory listings
Directory listings are a feature of web servers that allow users to view the contents of a directory on a server. By default, web servers often have directory listings enabled, which means that anyone who has access to the server can see all the files and directories in a given folder.
Turning off directory listings is important for API security because it prevents attackers from gaining access to sensitive files and directories on the server. If directory listings are enabled and an attacker gains access to the server, they can easily view and download any files that are not properly protected. By disabling directory listings, you can ensure that only authorized users can access the files and directories on the server.

View File

@@ -0,0 +1,5 @@
# Disable Entity Expansion
> Disable entity expansion if using XML, YML or any other language
Disabling entity expansion is important when using XML, YAML, or any other language that allows entities because it helps prevent XXE (XML External Entity) or YAML tag injection attacks. In these attacks, attacker normally injects some sort of custom code in the input to perform attacks against the application.. By disabling entity expansion, the input cannot be manipulated in this way, reducing the risk of such attacks.

View File

@@ -0,0 +1,7 @@
# Disable Entinty Parsing in XML
> Disable entity parsing if you are parsing XML to avoid XXE attacks
If the XML parser is vulnerable to XXE attacks, the attacker can use this vulnerability to read files on the server, perform SSRF attacks, and more. This can lead to the disclosure of sensitive information, denial of service, and other attacks.
XXE (XML External Entity) attack is a type of attack that targets applications that parse XML input from untrusted sources. In this attack, an attacker injects a malicious XML payload. This payload can contain external entities that the attacker can use to retrieve sensitive data, execute remote code, or launch denial of service attacks. XXE attacks can be prevented by disabling external entity processing or by validating and sanitizing the XML input before parsing it.

View File

@@ -0,0 +1,6 @@
# Endpoint Authentication
> Check if all the protected endpoints are behind authentication
> to avoid broken authentication process
By identifying and fixing broken authentication workflows, the API can prevent attacks such as brute force attacks, credential stuffing, session hijacking, and other authentication-related attacks. This can help ensure that the system is secure and that sensitive data is protected.

View File

@@ -0,0 +1,5 @@
# Force Content-Type
> Always force the `Content-Type` header to be set to relevant MIME type.
Forcing the content-type for API security is important because it ensures that the client and server are communicating in a mutually agreed-upon format for the data being transmitted. This can prevent attacks such as content spoofing or injection, where an attacker tries to trick the server into processing malicious content by pretending that it is of a different content type. By forcing the content-type to a specific format, the server can validate that the data it is receiving is legitimate and safe to process. Additionally, forcing the content-type can help prevent certain types of parsing errors that could be exploited by attackers.

View File

@@ -0,0 +1,5 @@
# JWT Secret
> You should have a good JWT secret to protect against token tempering as well as avoiding brute force attacks.
A strong secret key should be randomly generated, long, and complex, and should be stored securely and rotated periodically.

View File

@@ -0,0 +1,9 @@
# HSTS Header
> Use HSTS header with SSL to avoid SSL Strip attacks.
SSL strip is a type of attack where an attacker intercepts traffic between a client and a server that is meant to be secured by SSL/TLS encryption, and downgrades the connection to a plain text (non-encrypted) HTTP connection. This type of attack can go unnoticed by the user because the attacker is able to redirect the user to a look-alike website that also uses HTTP instead of HTTPS.
In an SSL strip attack, the attacker sets up a man-in-the-middle (MITM) position between the client and the server. When the client initiates a connection with the server, the attacker intercepts the SSL/TLS traffic and removes or replaces the HTTPS links with HTTP links. This can trick the user into thinking they are using a secure connection when in fact, they are not. The attacker can then monitor and manipulate the data transmitted between the client and server.
HSTS header is a security header that instructs browsers to only access the site over HTTPS. This header is used to prevent SSL Strip attacks. It is a good practice to use HSTS header with SSL.

View File

@@ -0,0 +1 @@
#

View File

@@ -0,0 +1,5 @@
# JWT Algorithm
> Do not extract the algorithm from the header, use backend.
Extracting the algorithm from the header of a JWT token can pose a security risk, as an attacker could modify the algorithm and potentially gain unauthorized access. It is therefore recommended to verify the algorithm on the backend rather than extracting it from the header. This can help ensure that the algorithm used to sign and verify the token is secure and has not been tampered with.

View File

@@ -0,0 +1,5 @@
# Jwt Payload
> Avoid storing sensitive data in JWT payload
Storing sensitive data in a JWT token payload can increase the risk of data breaches and other security incidents. If an attacker is able to obtain or tamper with the token, they could potentially access the sensitive data stored in the payload.

View File

@@ -0,0 +1,7 @@
# Max Retry/Jail in Login
> "Max Retry" and "jail" features are commonly used in login mechanisms to enhance security and prevent brute-force attacks.
**Max Retry:** The "Max Retry" feature limits the number of login attempts that a user can make within a specified time period. After a certain number of failed login attempts, the user is locked out of their account for a specified period of time, typically several minutes or hours. This helps to prevent brute-force attacks, where an attacker attempts to guess a user's password by making repeated login attempts. By limiting the number of attempts, the system can slow down or prevent such attacks.
**Jail:** The "jail" feature involves blocking IP addresses or user accounts that have exceeded the maximum number of failed login attempts within a certain time period. The blocked IP addresses or user accounts are prevented from attempting further logins for a specified period of time, typically several minutes or hours. This helps to prevent brute-force attacks, and also provides a mechanism to prevent malicious users from repeatedly attempting to access an account or system.

View File

@@ -0,0 +1,7 @@
# Monitor Everything
> Use agents to monitor all requests, responses and errors.
Using agents to monitor all requests, responses, and errors allows for real-time monitoring and detection of any abnormal activity or potential attacks. These agents can be configured to track metrics such as response times, error rates, and usage patterns, which can help identify any anomalies that could be indicative of an attack. By monitoring all requests and responses, the agents can provide visibility into the behavior of the API, which can help identify any potential security vulnerabilities or weaknesses. Additionally, agents can be used to log and analyze all data flowing through the API, which can be useful for debugging and auditing purposes.
To use agents for monitoring, a dedicated monitoring solution can be deployed alongside the API. This solution can be configured to capture data from all requests and responses, and analyze the data for any anomalies or issues. Agents can be implemented using various monitoring tools and technologies such as agents for application performance monitoring (APM), log monitoring, and network monitoring. The agents should be configured to provide real-time alerts to security teams if any suspicious activity is detected, allowing for immediate action to be taken.

View File

@@ -0,0 +1,7 @@
# X-Content-Type-Options: nosniff
> Send `X-Content-Type-Options: nosniff` header.
You should send the `X-Content-Type-Options: nosniff` header to prevent [MIME type sniffing attacks](https://www.keycdn.com/support/what-is-mime-sniffing) on your web application. This header tells the browser not to override the response content type even if it's not the expected type. For example, if an attacker manages to upload an HTML file with a disguised extension like .jpg, the server may still send the correct content type header for the HTML file. However, some browsers may ignore this header and try to "sniff" the content type based on the actual contents of the file, leading to a potential cross-site scripting (XSS) attack.
By sending the `X-Content-Type-Options: nosniff` header, you tell the browser to always trust the provided content type and not try to sniff the content type. This helps to mitigate the risk of attackers exploiting content type mismatches to deliver malicious content to unsuspecting users.

View File

@@ -0,0 +1,7 @@
# Non-Executable Stacks
> Use non-executable stacks to prevent attackers from executing code on your server.
A stack usually refers to the call stack or execution stack. It is a data structure used by the computer program to manage and keep track of the sequence of function calls, local variables, and other related data during the execution of the program.
A non-executable stack is a security mechanism that prevents malicious code from being executed by preventing the stack memory from being executed as code. This helps to prevent attacks such as buffer overflow attacks, where an attacker tries to overwrite the return address on the stack to redirect the program to execute malicious code. By using non-executable stacks, the program can keep the stack separate from executable code and help prevent these types of attacks.

View File

@@ -0,0 +1,9 @@
# redirect_uri
> Validate `redirect_uri on server-side to prevent open redirect attacks.
In OAuth, `redirect_uri` is a parameter that specifies the URI (Uniform Resource Identifier) that the authorization server should redirect the user to after authentication is complete. The `redirect_uri` is often used in the OAuth flow to return an authorization code or access token to the client application.
It is important to validate the `redirect_uri` on the server-side to prevent attacks such as open redirection attacks. In an open redirection attack, an attacker can modify the `redirect_uri` parameter to redirect the user to a malicious website. By validating the `redirect_uri` on the server-side, you can ensure that the redirect URI is a valid and authorized URI for the client application.
Validating the `redirect_uri` on the server-side can also prevent other types of attacks such as phishing attacks or cross-site request forgery (CSRF) attacks. By verifying that the `redirect_uri` matches a predefined list of authorized URIs, you can ensure that the user is redirected to a trusted site after authentication is complete.

View File

@@ -0,0 +1,9 @@
# Use State Param
> Use state parameter to avoid CSRF attacks
In OAuth, the `state` parameter is used as a security measure to prevent CSRF (Cross-Site Request Forgery) attacks. CSRF attacks occur when a malicious website or script sends a request to a legitimate website on behalf of a user who is currently authenticated.
To prevent CSRF attacks, the `state` parameter is used to store a unique value that is generated by the client application before initiating the authorization request. This value is included in the authorization request and then verified by the authorization server when the user is redirected back to the client application. If the `state` value in the authorization response matches the `state` value that was sent by the client application, the authorization is considered valid and the access token is returned to the client.
By using the `state` parameter, you can help to prevent malicious actors from intercepting or modifying the authorization request in transit, as the unique `state` value is only known to the client application and the authorization server. This can help to ensure the integrity and security of the OAuth flow and protect against CSRF attacks.

View File

@@ -0,0 +1,13 @@
# Validate Scope
> Have default scope, and validate scope for each application
In OAuth, scopes are used to specify the permissions and access levels that are granted to client applications when accessing protected resources on behalf of a user.
The best practice of having a default scope and validating the scope for each application is important because it helps to ensure that client applications only have access to the resources that they require, and that users are only granting the necessary permissions to each application.
The default scope is a set of permissions that are granted to all client applications by default, unless otherwise specified by the user. By having a default scope, you can ensure that all applications are subject to the same baseline security and access controls.
In addition to having a default scope, it is also recommended to validate the scope for each application. This means that when a user grants access to an application, the server should check to ensure that the requested scope is valid and appropriate for that application. This can help to prevent malicious applications from requesting excessive permissions or unauthorized access to user data.
By having a default scope and validating the scope for each application, you can help to ensure that the OAuth flow is secure and that client applications are only accessing the resources and permissions that they require.

View File

@@ -0,0 +1,5 @@
# Avoid Client-Side Encryption
> Use server-side encryption instead of client-side encryption
Client-side encryption is not recommended because client side codebase can be easily reverse engineered which can lead to the exposure of encryption algorithms.

View File

@@ -0,0 +1,5 @@
# JWT Payload Size
> Avoid storing large payloads in JWT tokens
A smaller payload can reduce network overhead, improve processing speed, and decrease the risk of attacks aimed at overloading the system.

Some files were not shown because too many files have changed in this diff Show More