<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">

    <title type="text">Next Direction</title>
    <subtitle type="text"><![CDATA[Posts - Neuigkeiten aus der Welt der Softwareentwicklung]]></subtitle>
    <link rel="alternate" type="text/html" href="https://next-direction.de/" />
    <link rel="self" type="application/atom+xml" href="https://next-direction.de/rss" />
    <updated>2026-01-10T13:39:03Z</updated>
    <rights>Copyright (c) 2026 Blog | Next Direction</rights>
    <generator uri="http://expressionengine.com/" version="7.5.18">ExpressionEngine</generator>
    <id>tag:next-direction.de:2026:01:10</id>

    <entry>
      <title><![CDATA[Where AI is going 2026]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/where-ai-is-going-2026" />
      <id>tag:https:2026:/next-direction.de/2.48</id>
      <published>2026-01-10T13:00:00Z</published>
      <updated>2026-01-10T13:39:03Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/wrongDirection.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/wrongDirection.jpg" alt="wrongDirection.jpg"/><br>
        <h1>Oh-My-OpenCode: Because Who Doesn&#8217;t Love Burning Through API Credits at Lightning Speed?</h1>

<p><em>A comprehensive exploration of the future of coding, where multiple AI agents collaborate beautifully to drain your wallet</em></p>

<hr />

<p>In the wild, wonderful world of AI-assisted coding, a new champion has emerged to save us all from the terrible burden of having money left in our bank accounts. Meet oh-my-opencode, &#8220;The Best Agent Harness&#8221; featuring Sisyphus: &#8220;The Batteries-Included Agent that codes like you.&#8221; And if that name doesn&#8217;t scream &#8220;Greek mythology about eternal, futile labor,&#8221; I don&#8217;t know what does. But hey, at least the irony is baked right into the branding!</p>

<p>With over 12,800 stars on GitHub, this project has clearly struck a chord with developers everywhere who looked at their API usage dashboard and thought, &#8220;You know what? These numbers are just too low. I need more agents talking to more agents, generating more tokens, accomplishing&#8230; well, <em>something</em>.&#8221;</p>

<h2>The Revolutionary Promise: Multi-Agent Orchestration (AKA Token Tornado)</h2>

<p>Oh-my-opencode lets you &#8220;run background agents, call specialized agents like oracle, librarian, and frontend engineer&#8221; while using &#8220;crafted LSP/AST tools, curated MCPs, and a full Claude Code compatibility layer.&#8221; Because why have one AI agent do something efficiently when you can have <em>multiple</em> agents pass the same task back and forth like a corporate email chain where everyone hits &#8220;reply all&#8221;?</p>

<p>The architecture is truly breathtaking. You&#8217;ve got your oracle agent, presumably consulting the ancient digital spirits about whether that function should return <code>null</code> or <code>undefined</code>. You&#8217;ve got your librarian agent, carefully cataloging all the documentation that the main agent could have just read directly. And let&#8217;s not forget the frontend engineer agent, which I can only assume spends most of its time debating whether to use <code>div</code> or <code>span</code> tags while racking up those sweet, sweet token charges.</p>

<p>It&#8217;s the coding equivalent of having a committee meeting where everyone needs to weigh in before you can decide what to have for lunch. Except this committee costs you $0.01 per opinion, and they <em>really</em> like to share their thoughts.</p>

<h2>The &#8220;$24,000 Promise&#8221; – Now That&#8217;s What I Call Quality Assurance!</h2>

<p>Here&#8217;s where it gets truly impressive. The project proudly declares it&#8217;s &#8220;Certified, Verified, Tested, Actually Useful Harness in Production, after $24,000 worth of tokens spent.&#8221; Twenty-four thousand dollars! That&#8217;s not a red flag; that&#8217;s a crimson banner waving majestically in the wind, visible from space.</p>

<p>Now, some cynics might argue that spending $24,000 in API costs during development suggests that maybe, <em>just maybe</em>, the system isn&#8217;t exactly optimized for token efficiency. But those people are clearly missing the point. This isn&#8217;t about efficiency; it&#8217;s about <em>thoroughness</em>. It&#8217;s about making absolutely certain that every possible way to consume tokens has been explored, tested, and implemented into the core functionality.</p>

<p>Think of it this way: if the developers burned through $24,000 in testing, just imagine how much you&#8217;ll get to spend in production! It&#8217;s like they&#8217;re providing a roadmap directly to API bankruptcy, and they&#8217;ve thoughtfully tested every step of the journey for you. How generous!</p>

<h2>&#8220;No Stupid Token Consumption&#8221; – The Punchline That Writes Itself</h2>

<p>In what can only be described as the comedy gold of technical documentation, the README boldly proclaims: &#8220;No stupid token consumption massive subagents here.&#8221; Let&#8217;s unpack this delicious piece of irony, shall we?</p>

<p>The project literally implements multiple specialized agents (oracle, librarian, frontend engineer, and presumably more) that coordinate with each other through a main orchestrator. Each agent needs its own context, its own reasoning loops, and its own API calls. When Agent A needs to consult Agent B, that&#8217;s double the context loading, double the token processing, and double the fun watching your API credits evaporate like morning dew in the Sahara.</p>

<p>But don&#8217;t worry, it&#8217;s not <em>stupid</em> token consumption. It&#8217;s <em>smart</em> token consumption. The kind where agents spawn subagents, which might spawn their own subagents, creating a beautiful fractal pattern of API calls that would make Benoit Mandelbrot weep with joy. The system even encourages spawning subagents &#8220;to save context&#8221;, because nothing says &#8220;context efficiency&#8221; like creating entirely new agent instances!</p>

<h2>The Greek Mythology We Deserved</h2>

<p>Let&#8217;s take a moment to appreciate the naming choice: Sisyphus. In Greek mythology, Sisyphus was condemned to roll a boulder up a hill for eternity, only to watch it roll back down each time he neared the top. The developers could have named it after Prometheus (who gave fire to humanity) or Athena (goddess of wisdom) or literally any other mythological figure associated with, you know, <em>success</em>.</p>

<p>But no, they chose Sisyphus, the poster child for futile, repetitive labor. It&#8217;s almost like they&#8217;re telling us something. Maybe it&#8217;s a subtle hint about the experience of watching multiple AI agents pass code back and forth, each one making tiny adjustments, each iteration consuming more tokens, the boulder of your feature request slowly rolling back down the hill as your API credits depleting faster than you can say &#8220;context window exceeded.&#8221;</p>

<p>The metaphor is so on-the-nose it&#8217;s practically doing a headstand.</p>

<h2>The Multi-Model Money Pit</h2>

<p>The project proudly supports &#8220;ChatGPT, Claude, Gemini SUBSCRIPTIONS&#8221; and claims &#8220;WE ALL COVER THEM.&#8221; Because why limit yourself to burning through credits with just one AI provider when you can establish a diversified portfolio of API expenditures?</p>

<p>It&#8217;s like going to an all-you-can-eat buffet, except instead of food, it&#8217;s AI models, and instead of getting full, you get broke. You can mix and match! Have Claude handle the orchestration, GPT-4 run your oracle agent, and Gemini&#8230; well, Gemini can just stand there looking pretty while consuming tokens in the background.</p>

<p>The beauty of this approach is that you&#8217;re not just dependent on one company&#8217;s pricing model, you&#8217;re dependent on <em>all</em> of them! When OpenAI raises their prices, no problem, you&#8217;re also paying Anthropic and Google. It&#8217;s the financial equivalent of not putting all your eggs in one basket, except every basket has a hole in it and the eggs are made of money and they&#8217;re all falling onto an expressway.</p>

<h2>The &#8220;Ultrawork&#8221; Philosophy: Because Regular Work Wasn&#8217;t Expensive Enough</h2>

<p>The documentation cryptically advises: &#8220;If this all seems overwhelming, just remember one thing: include the word ultrawork in your prompt.&#8221; Ah yes, &#8220;ultrawork&#8221;, not just work, not even extra work, but <em>ultra</em> work. Because regular work is for people who want to keep their API costs below their mortgage payment.</p>

<p>The term perfectly encapsulates the entire philosophy of the project. Why have an agent perform a task once when you can have it perform &#8220;ultrawork&#8221;, presumably involving multiple agents, multiple iterations, multiple model calls, and multiple opportunities to spend money? It&#8217;s not inefficiency; it&#8217;s &#8220;ultra-efficiency&#8221; so advanced that it loops back around to looking exactly like inefficiency.</p>

<h2>The Discord Community: A Support Group for the Token-Addicted</h2>

<p>Users are invited to &#8220;join our Discord community to connect with contributors and fellow oh-my-opencode users.&#8221; One can only imagine the conversations happening there:</p>

<p><em>&#8220;Hey guys, I just ran a simple refactoring task and my API bill was $47. Is that normal?&#8221;</em></p>

<p><em>&#8220;Only $47? Are you even using all the agents? Let me share my config where I have seven specialized agents working in parallel&#8230;&#8221;</em></p>

<p><em>&#8220;Check out this cool setup where the oracle agent consults the librarian agent, which spawns a research subagent, which calls back to the main orchestrator! My code quality went up 2% and my costs went up 300%!&#8221;</em></p>

<p>It&#8217;s probably the only developer community where bragging rights are inversely proportional to fiscal responsibility.</p>

<h2>The Industry&#8217;s Dirty Little Secret: Everyone&#8217;s Just Cooking With Water</h2>

<p>Here&#8217;s the thing that makes oh-my-opencode so perfectly representative of the current AI coding assistant landscape: <strong>everyone in the industry is just cooking with water</strong>. That&#8217;s a German saying, &#8220;mit Wasser kochen&#8221;, which means everyone&#8217;s using the same basic ingredients, the same basic methods, despite whatever mystique they try to project.</p>

<p>Every AI coding tool right now is essentially doing the same thing: making API calls to large language models. Some add a thin wrapper here, some add specialized prompts there, some create multi-agent architectures that sound impressive in blog posts. But fundamentally, they&#8217;re all just different ways to convert your money into API calls.</p>

<p>Oh-my-opencode&#8217;s genius, and I mean this with the most sarcastic possible tone, is that it&#8217;s completely transparent about this. While other tools try to hide their token consumption behind vague promises of &#8220;efficiency&#8221; and &#8220;optimization,&#8221; oh-my-opencode just&#8230; does it. Loudly. Proudly. With multiple agents. $24,000 worth of token consumption proudly displayed like a badge of honor.</p>

<p>It&#8217;s almost refreshing in its honesty. There&#8217;s no pretense here. No claims that their special sauce makes LLMs work 10x better. No proprietary magic that reduces token usage by 90%. Just pure, unfiltered, multi-agent token consumption served straight up. It&#8217;s like going to a restaurant where the menu openly admits &#8220;Our food is overpriced and mediocre, but at least we&#8217;re honest about it!&#8221;</p>

<h2>The Real Cost of &#8220;Free&#8221; Open Source</h2>

<p>The project is open source, which is wonderful. You don&#8217;t have to pay for the software itself! You just have to pay for:</p>

<ul>
<li>Claude API credits (unless you want to use that 20-request-per-day free tier)</li>
<li>GPT-4 API credits (because GPT-3.5 isn&#8217;t going to cut it for your oracle agent)</li>
<li>Gemini API credits (for good measure)</li>
<li>The inevitable therapy you&#8217;ll need after seeing your first monthly API bill</li>
<li>The financial advisor you&#8217;ll hire to explain where all your money went</li>
<li>The second mortgage on your house to fund your &#8220;simple refactoring project&#8221;</li>
</ul>

<p>But hey, the software itself? Totally free! It&#8217;s like offering someone free kindling for their fireplace, where the fireplace is made of hundred-dollar bills and the kindling is soaked in gasoline.</p>

<h2>The Perfect Storm of Modern Over-Engineering</h2>

<p>What makes oh-my-opencode truly special is how it perfectly encapsulates the current state of software development: complex multi-agent systems that sound impressive in architecture diagrams but in practice just multiply complexity and cost without proportional benefits.</p>

<p>We&#8217;ve entered an era where developers look at a problem that could be solved with a single, well-crafted prompt to an LLM and think, &#8220;You know what this needs? An orchestrator agent that delegates to specialized sub-agents that each maintain their own context and reasoning chains!&#8221; It&#8217;s like using a cruise missile to hang a picture frame. Sure, it&#8217;ll get the job done, but was it really necessary? And who&#8217;s paying for the missile?</p>

<p>The sad truth is that for most coding tasks, you don&#8217;t need multiple specialized agents deliberating in committee. You need one good AI model with proper context and a well-written prompt. But that doesn&#8217;t sound nearly as cool in a GitHub README, does it? &#8220;Simple, efficient, single-agent coding assistant&#8221; just doesn&#8217;t have the same pizzazz as &#8220;Multi-Agent Orchestrated AI Coding Harness with Background Task Delegation and Specialized Domain Agents.&#8221;</p>

<h2>The Emperor&#8217;s New Codebase</h2>

<p>Throughout tech history, we&#8217;ve seen this pattern repeat: new technology emerges, everyone rushes to make it as complicated as possible, and then eventually someone realizes that simple was better all along. We did it with microservices (take a working monolith, split it into 47 services that all need to talk to each other). We did it with blockchain (take a database, make it slower and more expensive). And now we&#8217;re doing it with AI coding assistants.</p>

<p>Oh-my-opencode is the emperor&#8217;s new codebase, everyone&#8217;s admiring the sophisticated architecture, the multiple agents, the clever orchestration, while quietly ignoring that it&#8217;s accomplishing similar results to much simpler solutions at multiples of the cost. With 12,800 stars, clearly a lot of people are buying into the vision. Or at least starring the repo, which costs nothing unlike actually using the tool.</p>

<h2>A Love Letter to Inefficiency</h2>

<p>In fairness to oh-my-opencode, they&#8217;re solving a real problem: the existing AI coding tools aren&#8217;t always great. The creator&#8217;s stated goals included being &#8220;free of cost concerns,&#8221; requiring &#8220;no tedious configuration,&#8221; and to &#8220;just work, and work well.&#8221; These are noble goals! The irony is that multi-agent architectures are, by their very nature, the opposite of &#8220;free of cost concerns.&#8221;</p>

<p>Every time you add another agent to your system, you&#8217;re adding:
- More context that needs to be loaded
- More token generation for inter-agent communication
- More API calls to coordinate activities
- More potential for cascading errors
- More points of failure
- More complexity to debug
- And most importantly, more cost</p>

<p>It&#8217;s like deciding to solve your high water bill by installing more faucets. Sure, you&#8217;ve got more options for where to get water now, but your bill isn&#8217;t going down.</p>

<h2>The Testimonials: When Marketing Meets Reality</h2>

<p>The README features glowing testimonials, including one that claims &#8220;If Claude Code does in 7 days what a human does in 3 months, Sisyphus does it in 1 hour.&#8221;</p>

<p>Let&#8217;s do some math here. If we take this at face value, and Claude Code costs roughly $0.10-0.30 per thousand tokens depending on the model, and a typical coding session might use 50,000-200,000 tokens&#8230; multiply that across your multi-agent architecture where each agent is maintaining its own context&#8230;</p>

<p>You know what? I don&#8217;t want to do this math. It&#8217;s too depressing. Let&#8217;s just say that yes, Sisyphus might do it in 1 hour instead of 7 days, but it&#8217;ll cost you enough that you might want to consider just hiring a human developer for those 7 days instead.</p>

<h2>The Future Is Multi-Agent (And Expensive)</h2>

<p>The real tragedy of oh-my-opencode isn&#8217;t that it&#8217;s a bad project, the developers clearly put significant work into it. The tragedy is that it represents where the industry is heading: increasingly complex systems that sound intelligent and sophisticated but fundamentally just create more abstraction layers between your wallet and the actual LLM providers.</p>

<p>We&#8217;re in a gold rush era where everyone&#8217;s trying to build the fanciest pickaxe, and nobody&#8217;s asking whether we actually need pickaxes at all when we&#8217;ve got excavators. Every new AI coding tool adds more features, more agents, more complexity, and somehow manages to use more tokens than the last one.</p>

<p>Oh-my-opencode is just honest about it. Brutally, $24,000-worth-of-testing honest about it.</p>

<h2>The Verdict: It&#8217;s Perfect! (If Money Is No Object)</h2>

<p>So, should you use oh-my-opencode? Absolutely! If you:</p>

<ul>
<li>Have disposable income you need to dispose of quickly</li>
<li>Think your API bills are too readable and need more digits</li>
<li>Enjoy explaining to your finance department why the &#8220;coding tool&#8221; line item exceeds your office rent</li>
<li>Find satisfaction in watching multiple AI agents have long conversations with each other about your code</li>
<li>Believe that more complexity always equals better results</li>
<li>Want to experience what it&#8217;s like to be Sisyphus, eternally pushing the boulder of multi-agent coordination up the hill of token consumption</li>
</ul>

<p>Then oh-my-opencode is absolutely the tool for you! With &#8220;no stupid token consumption&#8221; and &#8220;no bloat tools&#8221;, just smart token consumption and definitely-not-bloated multi-agent architectures!</p>

<h2>Conclusion: Embracing the Absurd</h2>

<p>At the end of the day, oh-my-opencode is a fascinating microcosm of where we are with AI tooling in 2026. We&#8217;ve got powerful language models that can assist with coding tasks, and we&#8217;ve managed to wrap them in so many layers of abstraction and complexity that we&#8217;ve multiplied the cost while providing&#8230; well, it depends on who you ask.</p>

<p>The project works. People use it. It has genuine fans. But the gap between the promise of AI-assisted coding (&#8220;save time and money!&#8221;) and the reality of multi-agent architectures (&#8220;watch your API bills soar!&#8221;) has never been more apparent.</p>

<p>Everyone in the industry really is just cooking with water. Some are using fancier pots, some are adding more burners, some are orchestrating multiple pots in parallel with specialized water-heating agents. But at the end of the day, we&#8217;re all just making API calls to the same handful of language models, watching the same tokens get consumed, and paying the same providers.</p>

<p>Oh-my-opencode&#8217;s greatest achievement might be serving as a mirror to the entire AI coding assistant industry: complicated, expensive, occasionally brilliant, often excessive, and somehow both the future of programming and a parody of it at the same time.</p>

<p>Just like Sisyphus eternally pushing his boulder, we&#8217;ll keep pushing our code up the hill of AI-assisted development, watching our tokens roll back down, and somehow convincing ourselves that this time, with just one more specialized agent, we&#8217;ll finally reach the top.</p>

<p><em>Your API credits will be sorely missed.</em></p>

<hr />

<p><strong>Disclaimer</strong>: oh-my-opencode is a real project made by real developers who put real effort into it. This post is satirical commentary on the broader trends in AI coding tools, not a serious technical review. If multi-agent token consumption is your jam, more power to you. Just remember to set up billing alerts.</p>

<p>Usually I show you the next direction, but I fear this is just the wrong direction, so please don&#8217;t follow it blindly!</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[AI-Powered Development: Months of Iteration]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/ai-powered-development-my-months-of-iteration-and-discovery" />
      <id>tag:https:2026:/next-direction.de/2.47</id>
      <published>2026-01-06T06:30:00Z</published>
      <updated>2026-01-10T13:12:29Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/aiblog.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/aiblog.jpg" alt="aiblog.jpg"/><br>
        <p>After several months of experimenting with AI-assisted development, I&#8217;ve finally settled into a workflow that feels natural, efficient, and actually sustainable. Like many developers exploring this space, I started with enthusiasm, tried every tool and methodology I could find, and eventually circled back to something simpler and more aligned with how I actually think and work.</p>

<p>This post is a reflection on that journey and a detailed look at the setup I&#8217;ve landed on. If you&#8217;re working with AI coding assistants and feeling overwhelmed by the options, or if you&#8217;re curious about what a practical, day-to-day AI development workflow looks like beyond the hype, this might resonate with you.</p>

<h2>The Foundation: Windows, WSL2, and VSCode</h2>

<p>Let me start with the basics. I work in a Windows environment, but like many modern developers, I&#8217;ve found that WSL2 (Windows Subsystem for Linux) gives me the best of both worlds. I can use Windows for my daily computing needs while having access to a full Linux environment for development. This setup has become incredibly stable over the years, and it handles the demands of AI-assisted development without breaking a sweat.</p>

<p>VSCode serves as my primary editor, which probably doesn&#8217;t surprise anyone. It&#8217;s become the de facto standard for a reason, the extension ecosystem is unmatched, the performance is solid, and the integration with WSL2 is seamless. I can edit files in my Linux filesystem with the full power of a native Windows application, and everything just works.</p>

<p>But here&#8217;s where my setup diverges from what you might expect: despite using VSCode, I don&#8217;t primarily rely on its AI extensions. Instead, I&#8217;ve gravitated toward using the Codex CLI for the bulk of my AI-assisted coding work.</p>

<h2>Why the CLI? Understanding My Tool Choice</h2>

<p>When I first started exploring AI coding assistants, I naturally installed the VSCode extensions that everyone was talking about. They&#8217;re convenient, they&#8217;re integrated, and they work reasonably well for many use cases. But after several weeks of use, I found myself increasingly drawn to the Codex CLI instead.</p>

<p>The CLI interface is simply more advanced and capable for the kind of work I do. It gives me more granular control over context, better handling of multi-file operations, and more sophisticated reasoning about my codebase. When I&#8217;m working on a complex feature that spans multiple files and requires understanding of intricate dependencies, the CLI&#8217;s ability to maintain that context and reason about it holistically makes a significant difference.</p>

<p>There&#8217;s also something to be said for the mental model of using a CLI tool. It creates a clearer separation between &#8220;I&#8217;m writing code&#8221; and &#8220;I&#8217;m collaborating with AI on code.&#8221; With an inline extension, these boundaries blur in ways that sometimes made me feel like I was fighting the tool rather than working with it. The CLI feels more like pair programming with a colleague, there&#8217;s a conversation happening, proposals being made, and I&#8217;m clearly the one deciding what gets implemented.</p>

<p>That said, VSCode remains essential to my workflow. I use it for editing, debugging, and all the traditional IDE tasks. The Codex CLI and VSCode complement each other beautifully, they each do what they&#8217;re best at, and I switch between them fluidly throughout my day.</p>

<h2>The Workflow: From Idea to Implementation</h2>

<p>Over the past month, my workflow has crystallized into four distinct phases. Each one leverages AI in different ways, and understanding when and how to use AI in each phase has been key to maintaining both productivity and code quality.</p>

<h3>Phase 1: Brainstorming and Ideation</h3>

<p>Every feature or project starts with brainstorming, and this is where AI has proven surprisingly valuable. I used to keep these sessions entirely in my head or in scattered notebook entries, but now I use AI as a sounding board for initial ideas.</p>

<p>The beauty of this phase is its low stakes. I&#8217;m not committing to code or architecture decisions yet; I&#8217;m just exploring possibilities. I&#8217;ll describe what I&#8217;m trying to build, the constraints I&#8217;m working within, and the problems I&#8217;m trying to solve. The AI helps me think through edge cases I might not have considered, suggests alternative approaches, and sometimes points out potential issues before I&#8217;ve written a single line of code.</p>

<p>What I appreciate most about using AI for brainstorming is that it&#8217;s infinitely patient with half-formed thoughts. I can ramble, contradict myself, change direction mid-sentence, and the AI simply rolls with it. It&#8217;s like having a colleague who never gets frustrated when you&#8217;re still figuring out what you&#8217;re trying to say.</p>

<p>This phase is purely conversational. I&#8217;m not generating code, I&#8217;m not making commitments, I&#8217;m just thinking out loud with something that can think back at me. By the end of a good brainstorming session, I usually have a much clearer picture of what I actually want to build and a few different approaches to consider.</p>

<h3>Phase 2: Planning and Task Definition</h3>

<p>Once I have a clear direction, I move into planning. This is where my workflow has evolved the most over the past month, and where I learned some important lessons about the difference between what sounds good in theory and what actually works for me in practice.</p>

<p>Initially, I tried using formal specification tools like SpecKit and BMAD. These tools promise structured, comprehensive planning with detailed specifications and clear acceptance criteria. In theory, they sound perfect, who wouldn&#8217;t want complete, unambiguous specs for their features?</p>

<p>In practice, I found them far too verbose for my needs. I&#8217;d spend an enormous amount of time writing specifications that felt more like documentation for an enterprise project than planning for actual implementation. The overhead was killing my momentum, and I&#8217;d finish a planning session feeling drained rather than energized about building.</p>

<p>So I simplified. Radically.</p>

<p>Now I use either backlog.md or vibe-kanban, depending on the project. What drew me to these tools is their philosophy: they&#8217;re full-featured task management systems, complete with kanban boards, milestone tracking, and task organization, but built on markdown and designed to stay out of your way rather than impose rigid workflows.</p>

<p>Backlog.md is particularly interesting because it gives you a proper task management system while keeping everything in readable, editable markdown files. I can organize tasks into sprints or milestones, move items between stages (backlog, in progress, done), add priorities and tags, and even track time estimates. But unlike heavyweight project management tools, I&#8217;m never locked into a particular structure. If I want to reorganize my milestones, create custom statuses, or completely restructure how I&#8217;m tracking work, I can do it instantly because it&#8217;s all just markdown under the hood.</p>

<p>The kanban view provides the visual organization I need to see what&#8217;s in flight and what&#8217;s coming next. The milestone tracking helps me group related tasks and maintain focus on larger goals. But there&#8217;s no ceremony, no mandatory fields, no fighting with a UI that assumes I work a certain way. I define the structure that makes sense for my project, and the tool adapts to me rather than the other way around.</p>

<p>Vibe-kanban takes a similar approach with its own spin on lightweight, markdown-based task management. It excels at giving you that visual kanban experience while maintaining the simplicity and control of working with plain text files. I can drag tasks between columns, nest subtasks, set due dates, and track progress, all the functionality you&#8217;d expect from a proper task manager, but without the bloat of enterprise tools that assume you&#8217;re managing a team of fifty.</p>

<p>The key insight here was realizing that I needed real task management functionality, kanban boards, milestone tracking, the ability to break down work and track progress, but I didn&#8217;t need the complexity and rigidity of traditional project management tools. These markdown-based systems give me the structure and features I need while maintaining the flexibility and control that keeps me productive.</p>

<p>When I work with AI during this phase, I use it to help break down larger features into implementable tasks. The AI is good at thinking through dependencies and suggesting a logical order of implementation. I can ask it to suggest milestone groupings or help me estimate the scope of different tasks. But the final task organization lives in my chosen system, where I maintain complete control and can iterate on the structure as I learn more about the project.</p>

<h3>Phase 3: Implementation with MCP Servers</h3>

<p>The implementation phase is where everything comes together, and it&#8217;s where my use of MCP (Model Context Protocol) servers really shines. I learned quickly that not every available tool needs to be in my toolkit. In fact, loading up too many MCP servers just adds noise and confusion.</p>

<p>I&#8217;ve settled on a small, carefully chosen set of MCP servers that each serve a specific purpose:</p>

<p><strong>Codanna</strong> handles code inspections and quality checks. When I&#8217;m implementing a feature, Codanna can analyze the code for potential issues, suggest improvements, and help ensure I&#8217;m following good practices. It&#8217;s like having a senior developer doing code review as I write, catching problems before they make it into a commit.</p>

<p><strong>Context7</strong> provides access to framework documentation. Rather than constantly switching to a browser to look up API references or check how a particular framework feature works, I can query Context7 directly through the CLI. This keeps me in flow and ensures the AI has access to accurate, up-to-date information about the frameworks I&#8217;m using.</p>

<p><strong>Playwright</strong> gives me browser control, which is essential for web development. I can automate testing, take screenshots, verify functionality, and even debug issues in real browsers without leaving my development environment.</p>

<p>Finally, I have MCP servers connected to my task management tools, whether that&#8217;s a custom server for my <code>backlog.md</code> or an integration with vibe-kanban. This lets the AI understand what I&#8217;m working on, check off completed tasks, and help me stay organized without manual context switching.</p>

<p>The restraint in this selection is deliberate. Early on, I had nearly a dozen MCP servers loaded, convinced that more tools meant more capability. Instead, it just meant more confusion, both for me and for the AI, which would sometimes suggest using the wrong tool or get overwhelmed by options.</p>

<p>With this focused set of tools, the AI can be genuinely helpful. It knows what&#8217;s available, knows what each tool does, and can make smart decisions about when to use them. The implementation phase becomes a smooth collaboration where the AI understands both my code and my workflow.</p>

<h3>Phase 4: Memory and Verification</h3>

<p>One of the challenges I encountered early on was that AI has no persistent memory of what we&#8217;ve done in previous sessions. Each conversation starts fresh, which is great for privacy but terrible for multi-day projects where context matters.</p>

<p>My solution is simple but effective: I maintain a markdown file that tracks the current technical progress. Think of it as a journal for the project, but written for the AI&#8217;s benefit as much as mine. After each significant session, I update this file with what we accomplished, decisions we made, and any important context that would be expensive to regenerate.</p>

<p>This file might include notes like &#8220;Implemented authentication using JWT with refresh tokens. Token expiry is 15 minutes, refresh is 7 days. Tokens stored in httpOnly cookies.&#8221; Or &#8220;Decided against using Redux for state management, project is small enough that Context API is sufficient.&#8221; These are the kinds of details that would take paragraphs to re-explain but are crucial for the AI to give good advice in future sessions.</p>

<p>When I start a new session, I simply provide this markdown file as context. The AI immediately understands where we are, what we&#8217;ve built, and why we made certain decisions. It&#8217;s like bringing a colleague up to speed with a quick status update rather than a lengthy meeting.</p>

<p>The other critical piece of this phase is testing. I write tests not just for the usual reasons, ensuring code works, preventing regressions, but also because tests are something the AI can run and verify. I can ask the AI to implement a feature, have it write or update tests, run those tests, and get objective feedback on whether the implementation is correct.</p>

<p>This verification loop is crucial. Without it, I&#8217;d need to manually test every AI-generated code suggestion, which would slow me down significantly. With good test coverage, I can move faster while maintaining confidence that things actually work.</p>

<p>Tests also serve as a form of specification that&#8217;s harder to misinterpret than English. I can tell the AI &#8220;make sure all these tests pass&#8221; and have much higher confidence in the result than if I&#8217;d just described the desired behavior in prose.</p>

<h2>Reflections and What I&#8217;ve Learned</h2>

<p>A month isn&#8217;t a long time in the grand scheme of things, but it&#8217;s been long enough to move past the initial excitement and hype and start understanding what actually works. A few key lessons have emerged:</p>

<p><strong>Simplicity beats features.</strong> The most sophisticated tools aren&#8217;t always the most useful. The lightweight, flexible solutions that adapt to my thinking often outperform the comprehensive, structured ones that try to impose their own methodology.</p>

<p><strong>The CLI is underrated.</strong> While everyone talks about inline coding assistants and IDE extensions, the CLI interface has proven more powerful and flexible for my needs. It&#8217;s worth trying even if it seems less convenient at first.</p>

<p><strong>Context is everything.</strong> Whether it&#8217;s through MCP servers, markdown memory files, or test suites, giving the AI the right context is far more important than having the most advanced model. A slightly older model with perfect context will outperform the latest model working blind.</p>

<p><strong>Less is more with tools.</strong> Curating a small set of well-understood tools beats having every possible option available. It reduces cognitive load and helps the AI make better decisions.</p>

<p>Looking ahead, I&#8217;m sure this workflow will continue evolving. New tools will emerge, I&#8217;ll discover better approaches, and my needs will change as I work on different types of projects. But I feel like I&#8217;ve found something sustainable, a workflow that enhances my productivity without trying to replace my judgment or push me into somebody else&#8217;s idea of how development should work.</p>

<p>If you&#8217;re building your own AI-assisted development workflow, I hope some of these ideas spark thoughts about what might work for you. The key is experimentation, willingness to change what isn&#8217;t working, and focusing on tools and processes that genuinely make you more effective rather than just more busy.</p>

<hr />

<p><em>What&#8217;s your AI development workflow look like? I&#8217;d love to hear what&#8217;s working for you, feel free to reach out and let me know.</em></p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Sails.js &amp; Vue.js - Part 4]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/sails-js-vue-js-part-4" />
      <id>tag:https:2025:/next-direction.de/2.46</id>
      <published>2025-11-23T11:00:00Z</published>
      <updated>2025-08-17T19:02:21Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-14.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-14.jpg" alt="part-14.jpg"/><br>
        <p>The fleet is assembled, the ships are enchanted, and the Chart Room is alive with data. But a true commander needs to see the entire theatre of operations at a single glance. Our components can communicate with the server, but they cannot yet speak to <em>each other</em>. It is time to establish a fleet-wide comms channel. It is time for the Admiral&#8217;s Spyglass.</p>

<hr />

<h3><strong>Part 4: The Admiral&#8217;s Spyglass (Advanced State &amp; Capstone)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Lord High Admiral</li>
<li><strong>Objective:</strong> To master global state management by integrating Pinia, allowing disconnected Vue components to share data. We will complete our journey by building a real-time fleet dashboard that updates instantly across the entire application using Sails Sockets.</li>
</ul>

<hr />

<h4><strong>Chief Architect&#8217;s Briefing (Background and Theory)</strong></h4>

<p>Our current architecture is powerful. Inertia passes data from a Sails controller directly to a page component as props. But what happens when a different, separate component needs that same data?</p>

<p>Imagine a <code>&lt;Navbar&gt;</code> component that needs to display the number of ships in the fleet, and a <code>&lt;FleetOverview&gt;</code> component that needs to list them. The data (<code>ships</code>) is passed to our main <code>Dashboard.vue</code> page. How do we get it to both the navbar and the overview without complex &#8220;prop drilling&#8221; (passing data down through layers of components)?</p>

<p>This is the role of a <strong>global state management library</strong>. <strong>Pinia</strong> is the current official, lightweight, and intuitive choice for Vue 3 <a href="https://frontendmasters.com/courses/vue-fundamentals/state-management-with-pinia/">frontendmasters.com</a>.</p>

<p>Think of a Pinia &#8220;store&#8221; as the ship&#8217;s Quartermaster. Instead of every crew member having their own small inventory list, there is one central Quartermaster who holds the master list for the entire ship. Any crew member, from any part of the ship, can ask the Quartermaster for information or ask them to update the master list.</p>

<p>Here’s how it works with Inertia:</p>

<ol>
<li><strong>Initial Load:</strong> Our Sails controller fetches the fleet data and passes it as a prop to our main page component (<code>Dashboard.vue</code>) via <code>res.inertia()</code>.</li>
<li><strong>Populate the Store:</strong> The <em>first thing</em> <code>Dashboard.vue</code> does with these props is hand them off to the Pinia store (the Quartermaster) to initialize the central state.</li>
<li><strong>Component Access:</strong> Now, any other component in our app (<code>Navbar.vue</code>, <code>FleetStatus.vue</code>, etc.) can completely ignore props and instead directly ask the Pinia store for the data it needs.</li>
<li><strong>Real-Time Updates:</strong> When our Sails app receives new information (e.g., a new ship is commissioned), it can use <strong>Sails Sockets</strong> to <code>blast</code> a message to all connected clients. A listener in our app will hear this message and tell the Pinia store to update its state. Because the store is reactive, every single component that uses its data will update automatically and instantly.</li>
</ol>

<p>This creates our capstone: a true real-time Single-Page Application.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>Pinia</code>: The official global state management library for Vue 3.</li>
<li><code>Store</code>: A Pinia container for state, getters, and actions.</li>
<li><code>defineStore</code>: The function used to create a new store.</li>
<li><code>State</code>: The raw, reactive data in a store (e.g., the array of ship objects).</li>
<li><code>Getters</code>: Computed values derived from state (e.g., the total count of ships).</li>
<li><code>Actions</code>: Functions that can modify the state (e.g., <code>addShip()</code>, <code>setShips()</code>).</li>
<li><code>Sails Sockets</code>: The real-time communication layer built into Sails.</li>
<li><code>sails.sockets.blast()</code>: A Sails method to broadcast a message to all connected clients.</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;The Fleet Command Dashboard&#8221;</strong></h4>

<p>We will build on our <code>inertia-flagship</code> project.</p>

<ul>
<li><p><strong>Task 1: Recruit the Quartermaster (Install Pinia)</strong></p>

<ol>
<li><p>In your <code>inertia-flagship</code> terminal, add Pinia to the project.</p>

<pre><code class="bash">npm install pinia
</code></pre></li>
</ol></li>
<li><p><strong>Task 2: Swear in the Quartermaster (Configure Pinia)</strong></p>

<ol>
<li><p>We need to tell our Vue app to use Pinia. Open <code>assets/js/app.js</code>.</p>

<pre><code class="javascript">// assets/js/app.js
import '../css/app.css';
import &#123; createApp, h &#125; from 'vue';
import &#123; createInertiaApp &#125; from '@inertiajs/vue3';
import &#123; createPinia &#125; from 'pinia'; // &lt;-- IMPORT THIS

const pinia = createPinia(); // &lt;-- CREATE THE INSTANCE

createInertiaApp(&#123;
  /* ... existing config ... */
  setup(&#123; el, App, props, plugin &#125;) &#123;
    createApp(&#123; render: () =&gt; h(App, props) &#125;)
      .use(plugin)
      .use(pinia) // &lt;-- TELL VUE TO USE IT
      .mount(el);
  &#125;,
&#125;);
</code></pre></li>
</ol></li>
<li><p><strong>Task 3: Create the Master Fleet Roster (The Pinia Store)</strong></p>

<ol>
<li>Create a new directory: <code>assets/js/stores/</code>.</li>
<li><p>Inside, create <code>fleetStore.js</code>. This will be our single source of truth for fleet data.</p>

<pre><code class="javascript">// assets/js/stores/fleetStore.js
import &#123; defineStore &#125; from 'pinia';

export const useFleetStore = defineStore('fleet', &#123;
  state: () =&gt; (&#123;
    ships: &#91;&#93;,
  &#125;),
  getters: &#123;
    shipCount: (state) =&gt; state.ships.length,
    isFleetReady: (state) =&gt; state.ships.length &gt; 0,
  &#125;,
  actions: &#123;
    setShips(initialShips) &#123;
      this.ships = initialShips;
    &#125;,
    addShip(newShip) &#123;
      this.ships.unshift(newShip); // Add new ships to the top
    &#125;
  &#125;,
&#125;);
</code></pre></li>
</ol></li>
<li><p><strong>Task 4: Prepare the Backend Data &amp; Real-Time Broadcast</strong></p>

<ol>
<li><p>Create a model, controller, and route for a list of ships.</p>

<pre><code class="bash">sails generate model Ship name:string class:string
sails generate controller DashboardController
</code></pre></li>
<li><p>In <code>config/routes.js</code>, add the dashboard routes:</p>

<pre><code class="javascript">'GET /dashboard': 'DashboardController.show',
'POST /ships': 'DashboardController.createShip',
</code></pre></li>
<li><p>In <code>api/controllers/DashboardController.js</code>, add the logic. Note the <code>sails.sockets.blast</code> call.</p>

<pre><code class="javascript">module.exports = &#123;
  show: async function (req, res) &#123;
    if (req.isSocket) &#123;
      // Join a room to hear about new ships
      sails.sockets.join(req, 'fleet-dashboard');
    &#125;
    const ships = await Ship.find().sort('createdAt DESC');
    return res.inertia('Dashboard', &#123; ships &#125;);
  &#125;,

  createShip: async function (req, res) &#123;
    const newShip = await Ship.create(&#123;
      name: req.body.name,
      class: req.body.class
    &#125;).fetch();

    // Broadcast the new ship data to all listening clients!
    sails.sockets.blast('ship_added', &#123; newShip &#125;);

    return res.ok(); // Just send a 200 OK, no redirect needed for API-like calls
  &#125;
&#125;;
</code></pre></li>
</ol></li>
<li><p><strong>Task 5: Build the Dashboard and Components</strong></p>

<ol>
<li><p>Create the main page component <code>assets/js/pages/Dashboard.vue</code>. Its only job is to initialize the store.</p>

<pre><code class="vue">&lt;script setup&gt;
import &#123; onMounted &#125; from 'vue';
import &#123; useFleetStore &#125; from '../stores/fleetStore';
import FleetStatus from '../components/FleetStatus.vue';
import ShipList from '../components/ShipList.vue';

// Get props from Inertia
const props = defineProps(&#123;
  ships: Array,
&#125;);

// Get a reference to our store
const fleetStore = useFleetStore();

// On initial load, populate the store with the data from the server
fleetStore.setShips(props.ships);

// Listen for real-time updates
onMounted(() =&gt; &#123;
  io.socket.on('ship_added', (&#123; newShip &#125;) =&gt; &#123;
    console.log('New ship received via socket!');
    fleetStore.addShip(newShip);
  &#125;);
&#125;);
&lt;/script&gt;

&lt;template&gt;
  &lt;h1&gt;Fleet Command Dashboard&lt;/h1&gt;
  &lt;FleetStatus /&gt;
  &lt;ShipList /&gt;
&lt;/template&gt;
</code></pre></li>
<li><p>Create <code>assets/js/components/FleetStatus.vue</code>. This component knows nothing about props; it only knows about the store.</p>

<pre><code class="vue">&lt;script setup&gt;
import &#123; useFleetStore &#125; from '../stores/fleetStore';
const fleetStore = useFleetStore();
&lt;/script&gt;
&lt;template&gt;
  &lt;div class="status-box"&gt;
    &lt;h3&gt;Fleet Status&lt;/h3&gt;
    &lt;p&gt;Total Ships: &lt;strong&gt;&#123;&#123; fleetStore.shipCount &#125;&#125;&lt;/strong&gt;&lt;/p&gt;
    &lt;p v-if="fleetStore.isFleetReady"&gt;Status: &lt;span class="ready"&gt;Ready for deployment!&lt;/span&gt;&lt;/p&gt;
    &lt;p v-else&gt;Status: &lt;span class="not-ready"&gt;No ships in fleet.&lt;/span&gt;&lt;/p&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre></li>
<li><p>Create <code>assets/js/components/ShipList.vue</code>, which also uses the store.</p>

<pre><code class="vue">&lt;script setup&gt;
import &#123; useFleetStore &#125; from '../stores/fleetStore';
const fleetStore = useFleetStore();
&lt;/script&gt;
&lt;template&gt;
  &lt;div class="list-box"&gt;
    &lt;h2&gt;Ship Roster&lt;/h2&gt;
    &lt;ul&gt;
      &lt;li v-for="ship in fleetStore.ships" :key="ship.id"&gt;
        &#123;&#123; ship.name &#125;&#125; (&#123;&#123; ship.class &#125;&#125;)
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre></li>
</ol></li>
<li><p><strong>Task 6: Test the Final System</strong></p>

<ol>
<li><code>sails lift</code> your application. Navigate to <code>http://localhost:1337/dashboard</code>. You should see the dashboard with &#8220;Total Ships: 0&#8221;.</li>
<li>Open a second browser window or tab and navigate to the same page.</li>
<li><p>In a terminal, use <code>curl</code> (or Postman/Insomnia) to simulate commissioning a new ship:</p>

<pre><code class="bash">curl -X POST http://localhost:1337/ships -d "name=The Endeavour&amp;class=Frigate"
</code></pre></li>
<li><strong>Witness the final magic:</strong> Instantly, without a page refresh, both browser windows will update. &#8220;Total Ships&#8221; will become 1, and &#8220;The Endeavour&#8221; will appear in the roster.</li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p><strong>Mission Accomplished, Lord High Admiral.</strong></p>

<p>You have reached the pinnacle of modern web application development. Your flagship is not just a collection of pages; it is a living, breathing application. Data flows seamlessly from the Sails database, through Inertia into a central Pinia store, and is shared across your entire Vue front-end. With Sails Sockets, that data is now updated in real-time, providing an instantaneous experience for all users.</p>

<p>You have mastered the entire process, from laying the first keel with <code>sails new</code> to commanding a real-time fleet with Pinia and Sockets. You are no longer just a captain; you are the architect and commander of a truly modern armada.</p>

<p>The seas of development are vast, but you now possess the charts, the tools, and the command experience to sail anywhere.</p>

<hr />

<h4><strong>Rewards</strong></h4>

<ul>
<li><strong>+1000 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You have built a full-stack, globally-managed, real-time single-page application.

<ul>
<li><strong>Final Badge Earned:</strong> <code>The Grand Fleet Admiral</code> 🎖️🌟</li>
</ul></li>
</ul>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Sails.js &amp; Vue.js - Part 3]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/sails-js-vue-js-part-3" />
      <id>tag:https:2025:/next-direction.de/2.45</id>
      <published>2025-11-16T11:00:00Z</published>
      <updated>2025-08-17T19:02:43Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-13.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-13.jpg" alt="part-13.jpg"/><br>
        <p>Excellent. The enchanted shipyard has provided us with a state-of-the-art vessel. Now it&#8217;s time to step inside and bring its instruments to life. We will begin with the most crucial room on any ship: the Chart Room.</p>

<hr />

<h3><strong>Part 3: The Living Chart Room (Building with Inertia &amp; Vue)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Lord High Admiral</li>
<li><strong>Objective:</strong> To transform a static information page into a dynamic, data-driven Vue component using Inertia, learning to pass data from Sails, navigate between pages without reloads, and submit forms.</li>
</ul>

<hr />

<h4><strong>Chief Architect&#8217;s Briefing (Background and Theory)</strong></h4>

<p>Our <code>inertia-flagship</code> is ready. The core task now is to build out a feature. We will create a &#8220;Chart Room&#8221; where the captain can view a list of known destinations and plot new ones.</p>

<p>This process will demonstrate the complete, magical end-to-end flow of the Modern Monolith:</p>

<ol>
<li>A user will click a <code>&lt;Link&gt;</code> component on our homepage to navigate to <code>/chartroom</code>.</li>
<li>Our Sails router (<code>config/routes.js</code>) will direct this request to a new <code>ChartRoomController</code>.</li>
<li>The controller will use a <code>Destination</code> model to fetch all known destinations from the database.</li>
<li>The controller will then pass this list of destinations as <em>props</em> to a Vue page component using <code>res.inertia('ChartRoom', &#123; destinations: allDestinations &#125;)</code>.</li>
<li>Our <code>ChartRoom.vue</code> component will receive the <code>destinations</code> array as a prop and use a <code>v-for</code> loop to display it.</li>
<li>Inside the <code>ChartRoom.vue</code> component, we will build a form. When submitted, this form will use an Inertia helper (<code>useForm</code>) to <code>POST</code> data to a new Sails endpoint. This helper gracefully handles loading states and validation errors for us.</li>
<li>The Sails controller action will process the form data, create a new <code>Destination</code> in the database, and then issue a <code>res.redirect('/chartroom')</code>.</li>
<li>Inertia intercepts this redirect, automatically re-fetches the props for <code>/chartroom</code> (which now include the new destination), and updates the page.</li>
</ol>

<p>Notice: no manual <code>fetch</code> calls, no manual JSON parsing, no manual page refreshing. It&#8217;s a seamless conversation between the backend and frontend.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>defineProps</code>: A Vue 3 <code>&lt;script setup&gt;</code> macro to declare the props a component expects to receive.</li>
<li><code>@inertiajs/vue3</code>: The official Inertia Vue 3 adapter, which provides core components and helpers.</li>
<li><code>&lt;Link&gt;</code> component: The Inertia replacement for <code>&lt;a&gt;</code> tags for client-side navigation.</li>
<li><code>useForm</code> helper: An Inertia hook for wrapping form data and simplifying submissions, including managing loading, error, and success states.</li>
<li>Server-Side Redirects: How Sails can tell Inertia to navigate to a new page alter an action (like a form submission).</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;Plotting the Course&#8221;</strong></h4>

<ul>
<li><p><strong>Task 1: Model the Destinations</strong></p>

<ol>
<li><p>First, our Chart Room needs data. Let&#8217;s create a model for our destinations.</p>

<pre><code class="bash">sails generate model Destination name:string eta:string
</code></pre>

<p>(ETA = Estimated Time of Arrival)</p></li>
</ol></li>
<li><p><strong>Task 2: Create the Controller &amp; Route</strong></p>

<ol>
<li><p>Generate the controller that will manage showing the chart room.</p>

<pre><code class="bash">sails generate controller ChartRoomController
</code></pre></li>
<li><p>Open <code>api/controllers/ChartRoomController.js</code> and create a <code>show</code> action. This action finds all destination data and passes it to a Vue component named <code>ChartRoom</code>.</p>

<pre><code class="javascript">module.exports = &#123;
  show: async function (req, res) &#123;
    const destinations = await Destination.find().sort('createdAt DESC');
    return res.inertia('ChartRoom', &#123; destinations &#125;);
  &#125;,
&#125;;
</code></pre></li>
<li><p>Now, wire up a route in <code>config/routes.js</code>. Add this line:</p>

<pre><code class="javascript">'GET /chartroom': 'ChartRoomController.show',
</code></pre></li>
</ol></li>
<li><p><strong>Task 3: Build the Chart Room Vue Component</strong></p>

<ol>
<li>Create the page component file: <code>assets/js/pages/ChartRoom.vue</code>.</li>
<li><p>Add the following code. This component defines <code>destinations</code> as a prop and then displays them in a list.</p>

<pre><code class="vue">&lt;script setup&gt;
import &#123; defineProps &#125; from 'vue';

defineProps(&#123;
  destinations: Array,
&#125;);
&lt;/script&gt;

&lt;template&gt;
  &lt;div class="container"&gt;
    &lt;h1&gt;The Chart Room&lt;/h1&gt;
    &lt;p&gt;A list of all known destinations in the fleet's database.&lt;/p&gt;

    &lt;div class="dest-list"&gt;
      &lt;h2&gt;Known Destinations&lt;/h2&gt;
      &lt;ul&gt;
        &lt;li v-for="dest in destinations" :key="dest.id"&gt;
          &lt;strong&gt;&#123;&#123; dest.name &#125;&#125;&lt;/strong&gt; (ETA: &#123;&#123; dest.eta &#125;&#125;)
        &lt;/li&gt;
      &lt;/ul&gt;
      &lt;p v-if="!destinations.length"&gt;No destinations plotted yet.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre></li>
</ol></li>
<li><p><strong>Task 4: Create Navigation to the Chart Room</strong></p>

<ol>
<li>We need a way to get to our new page. Open <code>assets/js/pages/Homepage.vue</code>.</li>
<li><p>Import the <code>&lt;Link&gt;</code> component and add a link to <code>/chartroom</code>.</p>

<pre><code class="vue">&lt;script setup&gt;
import &#123; Link &#125; from '@inertiajs/vue3'; // &lt;-- IMPORT THIS
&lt;/script&gt;

&lt;template&gt;
  &lt;!-- ... existing homepage content ... --&gt;
  &lt;Link href="/chartroom" class="button-link"&gt;Enter the Chart Room&lt;/Link&gt; &lt;!-- ADD THIS --&gt;
&lt;/template&gt;

&lt;style&gt;
  .button-link &#123; /* Add some basic styling */
    display: inline-block;
    padding: 10px 20px;
    margin-top: 20px;
    background-color: #007bff;
    color: white;
    text-decoration: none;
    border-radius: 5px;
  &#125;
&lt;/style&gt;
</code></pre></li>
</ol></li>
<li><p><strong>Task 5: Test the Data Flow</strong></p>

<ol>
<li>Lift your sails: <code>sails lift</code>.</li>
<li>Visit <code>http://localhost:1337</code>. Click the &#8220;Enter the Chart Room&#8221; link.</li>
<li>Observe: The URL changes to <code>/chartroom</code> and you see the new page <em>without a full page refresh</em>. It will say &#8220;No destinations plotted yet.&#8221; This confirms the link, controller, and component are all wired up correctly.</li>
</ol></li>
<li><p><strong>Task 6: Add the Form to Plot New Courses</strong></p>

<ol>
<li>Now for the interactive part. Let&#8217;s add a form to <code>assets/js/pages/ChartRoom.vue</code>.</li>
<li>Update the <code>&lt;script setup&gt;</code> section to import <code>useForm</code>.</li>
<li><p>Add the form and its submission logic.</p>

<pre><code class="vue">// In assets/js/pages/ChartRoom.vue
&lt;script setup&gt;
import &#123; defineProps &#125; from 'vue';
import &#123; useForm &#125; from '@inertiajs/vue3'; // &lt;-- Import useForm

defineProps(&#123;
  destinations: Array,
&#125;);

// Create a form object that wraps our input data
const form = useForm(&#123;
  name: '',
  eta: '',
&#125;);

// The function to call on form submission
const plotCourse = () =&gt; &#123;
  form.post('/destinations', &#123;
    // This runs after a successful submission
    onSuccess: () =&gt; form.reset('name', 'eta'),
  &#125;);
&#125;;
&lt;/script&gt;

&lt;template&gt;
  &lt;!-- ... The h1, p, and destination list from before ... --&gt;

  &lt;div class="new-dest-form"&gt;
    &lt;h2&gt;Plot a New Course&lt;/h2&gt;
    &lt;form @submit.prevent="plotCourse"&gt;
      &lt;div&gt;
        &lt;label for="name"&gt;Destination Name:&lt;/label&gt;
        &lt;input id="name" type="text" v-model="form.name" required&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;label for="eta"&gt;ETA:&lt;/label&gt;
        &lt;input id="eta" type="text" v-model="form.eta" required&gt;
      &lt;/div&gt;
      &lt;button type="submit" :disabled="form.processing"&gt;
        &#123;&#123; form.processing ? 'Plotting...' : 'Plot Course' &#125;&#125;
      &lt;/button&gt;
    &lt;/form&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre></li>
</ol></li>
<li><p><strong>Task 7: Create the Form-Handling Endpoint</strong></p>

<ol>
<li>The form <code>POST</code>s to <code>/destinations</code>. Let&#8217;s create that route and controller logic.</li>
<li><p>In <code>config/routes.js</code>, add the <code>POST</code> route:</p>

<pre><code class="javascript">'POST /destinations': 'ChartRoomController.createDestination',
</code></pre></li>
<li><p>In <code>api/controllers/ChartRoomController.js</code>, add the <code>createDestination</code> action:</p>

<pre><code class="javascript">module.exports = &#123;
  show: async function (req, res) &#123;
    // ... same as before
  &#125;,

  createDestination: async function(req, res) &#123;
    await Destination.create(&#123;
      name: req.body.name,
      eta: req.body.eta,
    &#125;);
    // This redirect is caught by Inertia, which then reloads the page's props!
    return res.redirect('/chartroom');
  &#125;,
&#125;;
</code></pre></li>
</ol></li>
<li><strong>Task 8: Final Test</strong>

<ol>
<li>Relaunch your app (<code>sails lift</code>).</li>
<li>Go to the Chart Room. Fill out the form (e.g., Name: &#8220;Tortuga&#8221;, ETA: &#8220;3 days&#8221;) and click &#8220;Plot Course&#8221;.</li>
<li><strong>Witness the magic:</strong> The form submits, the button briefly says &#8220;Plotting&#8230;&#8221;, and then the new destination instantly appears in the &#8220;Known Destinations&#8221; list above, without a full page reload.</li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Outstanding, Admiral. The Chart Room is now a living instrument. You have mastered the fundamental workflow of a modern Sails + Inertia application. You&#8217;ve passed data as props, navigated with <code>&lt;Link&gt;</code>, and handled form submissions with <code>useForm</code>, all while keeping your controller logic clean and familiar.</p>

<p>You can see how this pattern removes immense complexity. The front-end doesn&#8217;t need to know <em>how</em> to re-fetch the list; it just submits a form. The back-end doesn&#8217;t need to return special JSON; it just creates data and issues a standard redirect. Inertia handles the complex conversation between them, letting each side focus on what it does best.</p>

<hr />

<h4><strong>Rewards</strong></h4>

<ul>
<li><strong>+600 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You have built a fully functional, data-driven, interactive SPA page.

<ul>
<li><strong>Badge Earned:</strong> <code>The Living Cartographer</code> 🗺️✨</li>
</ul></li>
</ul>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Sails.js &amp; Vue.js - Part 2]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/sails-js-vue-js-part-2" />
      <id>tag:https:2025:/next-direction.de/2.44</id>
      <published>2025-11-09T11:00:00Z</published>
      <updated>2025-08-17T18:56:26Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-12.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-12.jpg" alt="part-12.jpg"/><br>
        <p>The traditional refit was a success, but it revealed the friction of the old ways. Now, we unleash the full power of the modern armada. We will cast the magical enchantment that fuses our Sails backend and Vue front-end into a single, seamless entity.</p>

<hr />

<h3><strong>Part 2: The Magical Enchantment (Introducing Inertia.js)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Lord High Admiral</li>
<li><strong>Objective:</strong> To understand what Inertia.js is, how it works, and to use the modern <code>create-sails</code> tool to scaffold a new Sails + Vue + Inertia project, creating the perfect foundation for our SPA.</li>
</ul>

<hr />

<h4><strong>Chief Architect&#8217;s Briefing (Background and Theory)</strong></h4>

<p>In our last mission, we had our Vue crew &#8220;shouting across the deck&#8221; to the Sails crew via API calls. It worked, but it was noisy and required building two separate communication systems.</p>

<p><strong>Inertia.js is the magical, telepathic link between the two crews.</strong></p>

<p>It&#8217;s not a framework; it&#8217;s a new approach to building classic server-driven web apps. It allows us to create fully client-side rendered, single-page apps without the complexity of building and consuming a separate API. As the team behind Inertia says, it&#8217;s &#8220;The Modern Monolith.&#8221;</p>

<p>Here&#8217;s how the magic works:</p>

<ol>
<li><strong>A Link is Clicked:</strong> A user clicks a special <code>&lt;Link&gt;</code> component in your Vue front-end (e.g., <code>&lt;Link href="/logbook"&gt;</code>).</li>
<li><strong>Inertia Intercepts:</strong> Instead of a full page reload, Inertia intercepts this click and makes a background request (an XHR/fetch request) to the Sails server.</li>
<li><strong>Sails Responds:</strong> Your Sails <code>config/routes.js</code> and controller work <em>exactly as they did before</em>. The controller fetches data from the database.</li>
<li><strong>The New Response:</strong> This is the key. Instead of calling <code>res.view('logbook', &#123; data &#125;)</code> or <code>res.json(&#123; data &#125;)</code>, your controller calls a new response: <code>res.inertia('LogbookPage', &#123; data &#125;)</code>.</li>
<li><strong>The Magic Payload:</strong> Sails sends back a special JSON response that contains two things: the name of the Vue component to render (<code>LogbookPage</code>) and the data as &#8220;props.&#8221;</li>
<li><strong>Vue Takes Over:</strong> Back in the browser, Inertia receives this payload, automatically finds the <code>LogbookPage.vue</code> component, and seamlessly swaps it into the view, passing the data directly to it as props.</li>
</ol>

<p>The result? You write your back-end logic in Sails controllers as you always have, and your front-end logic in self-contained Vue components. Inertia handles the &#8220;glue&#8221; in between. The official <code>inertia-sails</code> adapter is designed precisely for this, letting you &#8220;have the power of Sails and the client-side framework of your choice in one codebase&#8221; <a href="https://blog.sailscasts.com/good-bye-nuxt-hello-inertia-in-sails">blog.sailscasts.com</a>.</p>

<p>Setting this up manually is complex. Thankfully, the Sails community has provided a powerful shipwright&#8217;s tool to do it for us.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>Inertia.js</code>: The &#8220;glue&#8221; that connects a classic server-side framework (Sails) with a modern client-side framework (Vue).</li>
<li><code>Modern Monolith</code>: The architectural style of a single codebase that provides the feel of an SPA.</li>
<li><code>Adapter</code>: Server-side code (like <code>inertia-sails</code>) that teaches Sails how to speak the &#8220;Inertia language.&#8221;</li>
<li><code>res.inertia()</code>: The new response method used in controllers to render a client-side component.</li>
<li><code>Props</code>: The mechanism for passing data from a Sails controller directly to a Vue component.</li>
<li><code>&lt;Link&gt;</code>: The Inertia component that replaces traditional <code>&lt;a&gt;</code> tags for SPA navigation.</li>
<li><code>create-sails</code>: A scaffolding tool that generates a new Sails project pre-configured with Inertia and your choice of front-end framework <a href="https://docs.sailscasts.com/create-sails/what-is-create-sails">docs.sailscasts.com</a>.</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;The Enchanted Shipyard&#8221;</strong></h4>

<p>We will not refit our old ship. We will build a new one from the ground up using the most modern tools available, creating the perfect foundation.</p>

<ul>
<li><p><strong>Task 1: Commission the New Shipwright (<code>create-sails</code>)</strong></p>

<ol>
<li><p>This tool will build our project&#8217;s skeleton for us. In your terminal (outside of any existing project), run the following command:</p>

<pre><code class="bash">npx create-sails@latest
</code></pre></li>
</ol></li>
<li><p><strong>Task 2: Issue the Building Orders</strong></p>

<ol>
<li>The command will launch an interactive setup wizard. Answer the prompts as follows to build an Inertia-powered Vue ship:

<ul>
<li><code>What is the name of your app?</code>: <code>inertia-flagship</code></li>
<li><code>Choose a template</code>: <code>Web App</code></li>
<li><code>Which front-end framework would you like to use?</code>: <code>Vue</code></li>
</ul></li>
<li>The tool will now download all the necessary dependencies and configure the project. This may take a minute or two. Once finished, navigate into your new vessel: <code>cd inertia-flagship</code>.</li>
</ol></li>
<li><p><strong>Task 3: Inspect the Enchanted Blueprints</strong></p>

<ol>
<li>Open the <code>inertia-flagship</code> project in your code editor. You&#8217;ll notice some new and important files that <code>create-sails</code> built for us:

<ul>
<li><code>vite.config.js</code>: This configures <strong>Vite</strong>, a blazing-fast, modern build tool that now manages our front-end assets instead of the old Grunt-based system.</li>
<li><code>assets/js/app.js</code>: The main entry point for our Vue/Inertia application on the client-side.</li>
<li><code>assets/js/pages/</code>: <strong>This is where your Vue components will live.</strong> You&#8217;ll see a <code>Homepage.vue</code> already created.</li>
<li><code>api/responses/inertia.js</code>: The server-side code that provides the <code>res.inertia()</code> response.</li>
<li><code>views/layouts/app.ejs</code>: A very minimal layout file. Its only job is to provide a single <code>&lt;div id="app"&gt;</code> for Inertia to hook into.</li>
</ul></li>
</ol></li>
<li><p><strong>Task 4: The Maiden Voyage</strong></p>

<ol>
<li><p><code>create-sails</code> already ran <code>npm install</code> for you. All we need to do is lift the sails.</p>

<pre><code class="bash">sails lift
</code></pre></li>
<li>Open your browser to <code>http://localhost:1337</code>. You will see a stylish welcome page. This entire page, unlike our previous projects, is a Vue component (<code>Homepage.vue</code>) rendered by Inertia!</li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Magnificent! You have just constructed a state-of-the-art vessel&#8217;s foundation in a fraction of the time it would have taken manually. You didn&#8217;t just build a ship; you built an <em>enchanted shipyard</em>, a development environment where the back-end and front-end are perfectly integrated from day one.</p>

<p>You now have a project where:</p>

<ul>
<li>Vite provides lightning-fast development builds.</li>
<li>Sails handles routing and data logic.</li>
<li>Vue handles the view layer.</li>
<li>Inertia seamlessly bridges the two.</li>
</ul>

<p>We haven&#8217;t built any custom features yet, but we have established the most powerful foundation possible. In our next mission, we will use this enchanted vessel to begin replacing static pages with fully interactive, data-driven Vue components.</p>

<hr />

<h4><strong>Rewards</strong></h4>

<ul>
<li><strong>+500 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You have mastered the modern scaffolding process and understand the core theory of Inertia.js.

<ul>
<li><strong>Badge Earned:</strong> <code>The Shipwright Enchanter</code> ✨</li>
</ul></li>
</ul>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Sails.js &amp; Vue.js - Part 1]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/sails-js-vue-js-part-1" />
      <id>tag:https:2025:/next-direction.de/2.43</id>
      <published>2025-11-02T11:00:00Z</published>
      <updated>2025-08-17T18:50:49Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-11.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-11.jpg" alt="part-11.jpg"/><br>
        <p>Lord High Admiral. We will begin the refit. Before we outfit the fleet with the latest enchantments, a true commander must understand the traditional craft. This first step will ground us in the fundamentals of how a modern front-end communicates with our Sails vessel using the ship&#8217;s existing systems.</p>

<hr />

<h3><strong>Part 1: The Traditional Refit (Vue &amp; The Asset Pipeline)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Lord High Admiral</li>
<li><strong>Objective:</strong> To manually integrate a Vue.js component into a classic Sails EJS view, using the built-in asset pipeline and standard fetch requests to create a reactive island on a traditional web page.</li>
</ul>

<hr />

<h4><strong>Chief Architect&#8217;s Briefing (Background and Theory)</strong></h4>

<p>Our ships are currently built with EJS templates rendered on the server. When you visit <code>/logbook</code>, Sails compiles the HTML and sends a complete page to your browser. Our goal is to enhance these pages, not replace them entirely&#8230; yet.</p>

<p>We will treat Vue.js as a specialist crew member that we bring aboard to manage one specific, complex part of the deck. To do this, we will leverage Sails&#8217; built-in <strong>Asset Pipeline</strong>.</p>

<p>The <code>assets/</code> directory in your Sails project is a special place. Sails automatically processes the files within it. Any JavaScript file you place in <code>assets/js/</code> will be automatically included as a <code>&lt;script&gt;</code> tag in your master layout file (<code>views/layouts/layout.ejs</code>). This is the system Sails has always used for front-end JavaScript.</p>

<p>Our strategy is as follows:</p>

<ol>
<li>We will add the Vue.js library to our project via a CDN link in our layout file.</li>
<li>We will create a placeholder <code>&lt;div&gt;</code> in one of our EJS views. This will be the &#8220;station&#8221; where our Vue crew member works.</li>
<li>We will write our Vue application in a separate JavaScript file within <code>assets/js/</code>.</li>
<li>When the page loads, this script will find the placeholder <code>&lt;div&gt;</code> and &#8220;mount&#8221; our Vue application onto it, bringing that section of the page to life.</li>
<li>Once alive, our Vue component will make a standard network request (<code>fetch</code>) to a Sails API endpoint to get its data, just like any third-party client would.</li>
</ol>

<p>This approach creates a &#8220;reactive island&#8221; on an otherwise static page. It is powerful for enhancing specific components but, as we will see, it reveals why a more integrated solution like Inertia.js is so desirable for full applications.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>Asset Pipeline</code>: Sails&#8217; system in <code>assets/</code> for managing and serving front-end JavaScript and CSS.</li>
<li><code>layout.ejs</code>: The master view template where all scripts are loaded.</li>
<li><code>Vue CDN</code>: Including the Vue library directly from a Content Delivery Network for simplicity.</li>
<li><code>Mounting an App</code>: The core Vue concept of attaching a live application to a DOM element, e.g., <code>createApp().mount('#app')</code>.</li>
<li><code>AJAX / Fetch</code>: Standard browser APIs used by the front-end (Vue) to request JSON data non-blockingly from the back-end (Sails).</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;The Live Cargo Manifest&#8221;</strong></h4>

<p>We will create a new, simple application. The goal is to display a ship&#8217;s cargo manifest. The manifest list itself will be rendered and managed by Vue, fetching its data from a Sails API endpoint.</p>

<ul>
<li><p><strong>Task 1: Lay the New Keel</strong></p>

<ol>
<li><p>Create a fresh Sails project for this mission.</p>

<pre><code class="bash">sails new traditional-refit
cd traditional-refit
</code></pre></li>
</ol></li>
<li><p><strong>Task 2: Bring the Specialist Aboard (Add Vue.js)</strong></p>

<ol>
<li>Open the master layout file: <code>views/layouts/layout.ejs</code>.</li>
<li><p>Just before the closing <code>&lt;/body&gt;</code> tag, add the Vue.js script from its official CDN. Below that, we&#8217;ll link to our own future app script.</p>

<pre><code class="html">&lt;!-- ... existing scripts ... --&gt;

&lt;!--Add Vue.js from CDN--&gt;
&lt;script src="https://unpkg.com/vue@3/dist/vue.global.js"&gt;&lt;/script&gt;

&lt;!--Add our custom Vue application script--&gt;
&lt;script src="/js/manifest-app.js"&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;
</code></pre></li>
</ol></li>
<li><p><strong>Task 3: Prepare the Workstation (The Mount Point)</strong></p>

<ol>
<li>Open the default homepage view: <code>views/pages/homepage.ejs</code>.</li>
<li><p>Replace its content with a simple structure, including the <code>&lt;div id="manifest-app"&gt;</code> where our Vue app will live.</p>

<pre><code class="html">&lt;div id="manifest-app"&gt;
  &lt;h1&gt;Live Cargo Manifest&lt;/h1&gt;
  &lt;div v-if="loading"&gt;Loading manifest from the ship's hold...&lt;/div&gt;
  &lt;ul v-else&gt;
    &lt;li v-for="item in cargo" :key="item.id"&gt;
      &#123;&#123; item.name &#125;&#125; - Quantity: &#123;&#123; item.quantity &#125;&#125;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;
</code></pre>

<p>Notice the Vue-specific syntax like <code>v-if</code> and <code>v-for</code>. This will be inert HTML until Vue takes control of it.</p></li>
</ol></li>
<li><p><strong>Task 4: Create the API Endpoint</strong></p>

<ol>
<li><p>Generate a new controller to serve our manifest data.</p>

<pre><code class="bash">sails generate controller ManifestController
</code></pre></li>
<li><p>Open <code>api/controllers/ManifestController.js</code> and create an action to return JSON data.</p>

<pre><code class="javascript">module.exports = &#123;
  get: function(req, res) &#123;
    const cargoData = &#91;
      &#123; id: 1, name: 'Barrels of Rum', quantity: 50 &#125;,
      &#123; id: 2, name: 'Crates of Cannonballs', quantity: 100 &#125;,
      &#123; id: 3, name: 'Secret Treasure Maps', quantity: 5 &#125;
    &#93;;
    return res.json(cargoData);
  &#125;
&#125;;
</code></pre></li>
<li><p>Create a route for this endpoint in <code>config/routes.js</code>:</p>

<pre><code class="javascript">'GET /api/v1/manifest': 'ManifestController.get',
</code></pre></li>
</ol></li>
<li><p><strong>Task 5: Write the Vue App Logic</strong></p>

<ol>
<li>Create a new file: <code>assets/js/manifest-app.js</code>.</li>
<li><p>Add the Vue code that will mount to our div, fetch data from the API, and manage the state.</p>

<pre><code class="javascript">const &#123; createApp &#125; = Vue;

createApp(&#123;
  data() &#123;
    return &#123;
      cargo: &#91;&#93;,
      loading: true
    &#125;
  &#125;,
  async mounted() &#123;
    console.log('Vue app has mounted, fetching manifest...');
    const res = await fetch('/api/v1/manifest');
    this.cargo = await res.json();
    this.loading = false;
  &#125;
&#125;).mount('#manifest-app');
</code></pre></li>
</ol></li>
<li><p><strong>Task 6: Hoist the Sails</strong></p>

<ol>
<li>Start your server: <code>sails lift</code>.</li>
<li>Open your browser to <code>http://localhost:1337</code>.</li>
<li>You will briefly see &#8220;Loading manifest&#8230;&#8221; and then, almost instantly, the list of cargo items will appear. This list was not rendered by EJS; it was rendered on the client-side by Vue after it fetched data from your Sails API.</li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Excellent work, Admiral. You have successfully refitted a traditional vessel with a modern, reactive component. You now understand the fundamental mechanics: a server-rendered page loads, a JavaScript library is included, it mounts to a DOM element, and it fetches data from an API to manage its own small piece of the world.</p>

<p>However, consider the complexities. We had to manually create API endpoints for our front-end. We have Vue syntax sitting inside an EJS file. We wrote a manual <code>fetch</code> call. If we wanted to add a form to add new cargo, we&#8217;d have to write more manual <code>fetch</code> calls with <code>POST</code> requests and handle the response.</p>

<p>This approach works, but it feels&#8230; disconnected. It&#8217;s like our specialist Vue crew has to shout across the deck to the Sails crew to get anything done. What if they could communicate telepathically?</p>

<p>This is the very problem that Inertia.js was born to solve.</p>

<hr />

<h4><strong>Rewards</strong></h4>

<ul>
<li><strong>+400 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You have successfully bridged the gap between a classic back-end and a modern front-end framework.

<ul>
<li><strong>Badge Earned:</strong> <code>The Traditional Refitter</code> 🛠️</li>
</ul></li>
</ul>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Learn Sails.js - Part 10]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/learn-sails-js-part-10" />
      <id>tag:https:2025:/next-direction.de/2.42</id>
      <published>2025-10-26T11:00:00Z</published>
      <updated>2025-08-17T18:33:24Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-10.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-10.jpg" alt="part-10.jpg"/><br>
        <p>You have mastered this vessel and are ready for the final trial. It is time to step onto the quarterdeck, take the spyglass, and look toward the horizon. This isn&#8217;t just about one ship anymore; it&#8217;s about commanding a fleet.</p>

<hr />

<h3><strong>Part 10: Becoming a Fleet Admiral (Capstone Project &amp; Next Steps)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Captain</li>
<li><strong>Objective:</strong> To synthesize all acquired knowledge by building a complete Sails.js application from scratch, solidifying your skills and creating a flagship project for your portfolio.</li>
</ul>

<hr />

<h4><strong>The Captain&#8217;s Briefing (Background and Theory)</strong></h4>

<p>Captain, you began this journey with nothing but an empty hull (<code>sails new</code>). You learned to frame the ship with MVC, stock its holds with Models and Waterline, and hire a specialist crew with Services. You installed a magical telegraph with WebSockets, posted guards with Policies, drilled your crew with automated Tests, and finally charted a course for the open sea of production.</p>

<p>This part is the culmination of that professional development. There are no new concepts to learn. Instead, this is your final examination, a solo voyage where you will apply everything you have learned to build your own flagship. This is where theory becomes practice, where knowledge becomes wisdom. You will face challenges and have to consult the charts (the documentation), but that is the essence of command.</p>

<p>By completing this capstone project, you will have more than just a passing knowledge of Sails; you will have tangible proof of your ability to architect, build, secure, and deploy a modern, real-time web application. This project becomes the crown jewel of your fleet, a testament to your skill that you can show to others.</p>

<p>After this, the world is yours. We will look at where you can sail next, from exploring advanced shipwrighting with custom hooks to forming alliances with powerful front-end frameworks.</p>

<hr />

<h4><strong>Mastery Checklist (Concepts You Command)</strong></h4>

<p>You are now proficient in the following arts of web development:</p>

<ul>
<li><strong>MVC Architecture:</strong> Organizing code into Models, Views, and Controllers.</li>
<li><strong>Waterline ORM:</strong> Defining models, associations, and validations.</li>
<li><strong>Blueprint API:</strong> Leveraging automatic CRUD routes for rapid development.</li>
<li><strong>Custom Controllers &amp; Actions:</strong> Writing bespoke logic for requests.</li>
<li><strong>Services:</strong> Encapsulating reusable business logic.</li>
<li><strong>WebSockets &amp; <code>sails.io.js</code>:</strong> Implementing real-time communication.</li>
<li><strong>Authentication:</strong> Managing user sessions and securely handling passwords.</li>
<li><strong>Policies &amp; Authorization:</strong> Securing endpoints with access control rules.</li>
<li><strong>Automated Testing:</strong> Writing unit tests with Mocha to ensure code quality.</li>
<li><strong>Environment Configuration:</strong> Managing settings for development and production.</li>
<li><strong>Deployment:</strong> Launching a production-ready application with a process manager.</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;The Fleet Admiral&#8217;s Flagship&#8221;</strong></h4>

<p>Your orders are to construct a new ship, superior to any you&#8217;ve worked on before. This will be your personal flagship.</p>

<p><strong>The Mission:</strong> Build a &#8220;Live Fleet Log.&#8221; This application will allow multiple registered captains to post log entries, which will then appear in a real-time feed for all connected users to see.</p>

<p><strong>Admiral&#8217;s Orders (Project Requirements):</strong></p>

<ol>
<li><strong>From Scratch:</strong> Create a new Sails project: <code>sails new flagship</code>.</li>
<li><strong>Models &amp; Associations:</strong>

<ul>
<li>Create a <code>User</code> model (<code>emailAddress</code>, <code>fullName</code>, <code>encryptedPassword</code>).</li>
<li>Create a <code>LogEntry</code> model (<code>text</code>, <code>priority</code>).</li>
<li>Establish a one-to-many association: A <code>User</code> can have many <code>LogEntry</code> records.</li>
</ul></li>
<li><strong>Authentication &amp; Security:</strong>

<ul>
<li>Implement user signup, login, and logout. Passwords must be hashed.</li>
<li>Use policies to ensure that only logged-in users can create new log entries.</li>
<li>A user should only be able to edit or delete <em>their own</em> log entries.</li>
</ul></li>
<li><strong>Services:</strong>

<ul>
<li>Create a <code>FormattingService</code> with a method like <code>formatTimestamp(date)</code> that you can use to make the timestamps on log entries look nice.</li>
</ul></li>
<li><strong>Real-Time Dashboard:</strong>

<ul>
<li>Create a main dashboard page that is visible to everyone (even logged-out users).</li>
<li>When a user creates a new <code>LogEntry</code>, it should be broadcast via WebSockets and appear instantly at the top of the dashboard on <em>every</em> connected client&#8217;s browser without a page refresh.</li>
</ul></li>
<li><strong>Automated Drills:</strong>

<ul>
<li>Write at least one unit test for your <code>FormattingService</code>.</li>
<li>Write at least one &#8220;request&#8221; test that attempts to create a log entry without being logged in and asserts that it is forbidden.</li>
</ul></li>
</ol>

<p><strong>Bonus Challenge: &#8220;Beyond the Horizon&#8221;</strong></p>

<ul>
<li><strong>Integrate a Third-Party API:</strong> A key skill for any developer is integrating external services. Add a feature where, upon creating a log entry, you use its text to call out to an external API.

<ul>
<li><strong>Idea 1:</strong> Use a weather API to attach the current &#8220;sea conditions&#8221; to every new log entry.</li>
<li><strong>Idea 2:</strong> Use a sentiment analysis API to determine if a log entry is &#8220;positive,&#8221; &#8220;negative,&#8221; or &#8220;neutral&#8221; and display an icon next to it.</li>
</ul></li>
<li>This will require you to use an HTTP request library like <code>axios</code> (<code>npm install axios</code>) inside a Sails action or service.</li>
</ul>

<hr />

<h4><strong>Mission Debrief (A Captain&#8217;s Legacy)</strong></h4>

<p>Upon completing this quest, you have not just finished a tutorial; you have built a complex, portfolio-worthy application. You have faced the challenges of development head-on and emerged victorious. Your flagship is complete. The title is earned.</p>

<p><strong>Congratulations, Fleet Admiral.</strong></p>

<p>Your journey with Sails is just beginning. The charts are now yours to read.</p>

<p><strong>Where to Sail Next:</strong></p>

<ul>
<li><strong>Study the Official Charts (Sails Docs):</strong> The official Sails.js documentation is your bible. Dig into concepts you&#8217;re curious about, like custom hooks, file uploads, and advanced model settings.</li>
<li><strong>Advanced Shipwrighting (Custom Hooks):</strong> Learn to build your own custom hooks to add reusable functionalities, like a hook that connects to a special service on every startup.</li>
<li><strong>Form Alliances (Front-End Frameworks):</strong> Learn how to use Sails as a pure API backend for a powerful front-end fleet like React, Vue, or Angular.</li>
<li><strong>Join the Council of Admirals (The Community):</strong> Engage with the Sails community on GitHub or other forums. Contribute to the open-source project, help others, and continue to learn from your fellow captains.</li>
</ul>

<hr />

<h4><strong>Final Rewards</strong></h4>

<ul>
<li><strong>+1000 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You have achieved total mastery, from building the hull to commanding the fleet. You are a true Sails.js architect.

<ul>
<li><strong>Badge Earned:</strong> <code>Fleet Admiral</code> 🎖️</li>
</ul></li>
</ul>

<p>Fair winds and following seas, Admiral. Your fleet awaits its orders.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Learn Sails.js - Part 9]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/learn-sails-js-part-9" />
      <id>tag:https:2025:/next-direction.de/2.41</id>
      <published>2025-10-19T10:00:00Z</published>
      <updated>2025-08-17T18:32:52Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-9.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-9.jpg" alt="part-9.jpg"/><br>
        <p>All preparations are complete. The crew is drilled, the ship is secure, and the holds are managed. But we are still anchored in a safe, familiar harbor. The time has come to chart a course for the open sea, the unpredictable, high-performance world of production.</p>

<hr />

<h3><strong>Part 9: Charting the Open Seas (Configuration &amp; Deployment)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> First Mate</li>
<li><strong>Objective:</strong> To prepare your Sails application for a live production environment by managing environment-specific configurations and learning the process of deploying it to a server.</li>
</ul>

<hr />

<h4><strong>The Captain&#8217;s Briefing (Background and Theory)</strong></h4>

<p>So far, we have operated in a private shipyard (<code>localhost</code>). Here, the waters are calm. Mistakes are cheap, our tools are laid out for us (<code>sails lift</code> auto-restarts the server), and performance isn&#8217;t a primary concern.</p>

<p>The open sea, a <strong>production environment</strong>, is different. It&#8217;s treacherous and demanding. Visitors expect a ship that is fast, efficient, and always available. We can&#8217;t have our First Mate on the deck constantly restarting the engine by hand. The ship must be configured to be lean, resilient, and ready for battle.</p>

<p><strong>Environment-Specific Blueprints</strong></p>

<p>Sails has a brilliant system for this: the <code>config/env/</code> directory. Sails knows which environment it&#8217;s in based on a system-level flag called <code>NODE_ENV</code>.</p>

<ul>
<li>When you run <code>sails lift</code>, by default <code>NODE_ENV</code> is <code>development</code>. Sails will look for a file at <code>config/env/development.js</code> and merge its settings.</li>
<li>When you run your app for production, you set <code>NODE_ENV</code> to <code>production</code>. Sails then looks for <code>config/env/production.js</code> and uses its settings instead.</li>
</ul>

<p>This allows you to have completely different configurations for your shipyard and your sea voyages. Common differences include:</p>

<ul>
<li><strong>Datastores:</strong> Using the temporary <code>sails-disk</code> in development, but a powerful, persistent database like PostgreSQL in production.</li>
<li><strong>Logging:</strong> Verbose, detailed logs in development to help you debug, but quieter, more essential logs in production to save resources.</li>
<li><strong>Security:</strong> Using different API keys, secrets, and credentials. You must <strong>never</strong> commit production secrets to your code repository. They should be set as environment variables on the server itself, which <code>production.js</code> can then read.</li>
<li><strong>Performance:</strong> Sails automatically does amazing things in production mode, like <strong>minifying</strong> your CSS and JavaScript files and <strong>bundling</strong> them together into single files. This makes your ship &#8220;lighter&#8221; and drastically reduces page load times for your visitors.</li>
</ul>

<p><strong>Launching the Fleet: The Dockmaster (PM2)</strong></p>

<p>You don&#8217;t just run <code>sails lift</code> on a production server and walk away. What happens if the app crashes? Who restarts it? This is the job of a <strong>Process Manager</strong>.</p>

<p>A process manager is a dockmaster for your live application. The most popular one for Node.js apps is <strong>PM2</strong>. It&#8217;s a simple tool that you run your app with. It will:</p>

<ul>
<li>Keep your app running in the background.</li>
<li>Automatically restart the app if it ever crashes.</li>
<li>Help you run your app on multiple CPU cores for better performance.</li>
<li>Manage and rotate log files.</li>
</ul>

<p>The command to launch your ship for its maiden voyage will be <code>sails lift --prod</code>. The <code>--prod</code> flag is the crucial signal that tells Sails to enable all its production optimizations and use your <code>production.js</code> configuration file.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>Environment Configuration</code>: Using different settings for development, testing, and production.</li>
<li><code>NODE_ENV</code>: The environment variable that tells Sails which mode to run in.</li>
<li><code>config/env/production.js</code>: The file containing all settings overrides for a live environment.</li>
<li><code>Asset Minification/Bundling</code>: Sails&#8217; automatic process of optimizing CSS/JS for production.</li>
<li><code>Process Manager</code>: A tool (like <code>PM2</code>) that keeps your live application running reliably.</li>
<li><code>sails lift --prod</code>: The command to start Sails in production mode.</li>
<li><code>Secrets Management</code>: The practice of keeping sensitive data like API keys out of your code.</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;The Maiden Voyage Simulation&#8221;</strong></h4>

<p>We will prepare our ship for launch, configure its production-ready blueprints, and use a professional process manager to launch it, all safely on our local machine.</p>

<ul>
<li><p><strong>Task 1: Prepare the Production Blueprints</strong></p>

<ol>
<li>In your <code>config/</code> directory, create a new folder named <code>env</code>.</li>
<li>Inside <code>config/env/</code>, create a new file named <code>production.js</code>.</li>
<li><p>Add the following configuration. This tells Sails how to behave on the open sea.</p>

<pre><code class="javascript">// config/env/production.js
module.exports = &#123;
  datastores: &#123;
    /***************************************************************************
    *                                                                          *
    * In production, you'll want to use a real database like PostgreSQL or     *
    * MySQL. This is where you'd configure the connection settings.            *
    *                                                                          *
    ***************************************************************************/
    // default: &#123;
    //   adapter: 'sails-postgresql',
    //   url: 'YOUR_PRODUCTION_DATABASE_URL',
    //   ssl: true,
    // &#125;,
  &#125;,

  log: &#123;
    // In production, we only want to log 'info' level messages and above
    level: 'info'
  &#125;,

  session: &#123;
    // A secure, long, random secret for signing session cookies.
    // Replace this with your own secret!
    secret: 'a4f434a2a163aed4b72449856f6b158b'
  &#125;
&#125;;
</code></pre></li>
</ol></li>
<li><p><strong>Task 2: Install the Dockmaster (PM2)</strong></p>

<ol>
<li><p>PM2 is a command-line tool that should be installed globally. In your terminal, run:</p>

<pre><code class="bash">npm install pm2 -g
</code></pre></li>
</ol></li>
<li><p><strong>Task 3: Simulate the Maiden Voyage</strong></p>

<ol>
<li>We will now launch our ship in production mode. Notice the difference in the log output compared to <code>sails lift</code>.</li>
<li><p>In your terminal, run:</p>

<pre><code class="bash">sails lift --prod
</code></pre>

<p>You&#8217;ll see much less chatter, just the essential &#8220;ship is online&#8221; message. The log level from <code>production.js</code> is working! Press <code>CTRL+C</code> to stop it.</p></li>
</ol></li>
<li><p><strong>Task 4: Launch with the Dockmaster</strong></p>

<ol>
<li>This is the professional way to launch. We tell PM2 to start our app.</li>
<li><p>Run this command from your project root:</p>

<pre><code class="bash">pm2 start app.js --name "the-sea-serpent" -- --prod
</code></pre>

<ul>
<li><code>pm2 start app.js</code>: The basic command to start the app.</li>
<li><code>--name "the-sea-serpent"</code>: Gives our running process a memorable name.</li>
<li><code>-- --prod</code>: This is critical. The double-dash (<code>--</code>) tells PM2 to pass the <code>--prod</code> flag directly to our <code>app.js</code> script, putting Sails in production mode.</li>
</ul></li>
</ol></li>
<li><p><strong>Task 5: Manage Your Fleet</strong></p>

<ol>
<li>Your app is now running in the background! How do we check on it?</li>
<li><p><strong>List running processes:</strong></p>

<pre><code class="bash">pm2 list
</code></pre>

<p>You will see &#8220;the-sea-serpent&#8221; in the list with a status of &#8220;online&#8221;.</p></li>
<li><p><strong>Check the ship&#8217;s logs:</strong></p>

<pre><code class="bash">pm2 logs the-sea-serpent
</code></pre></li>
<li><p><strong>Stop the ship:</strong></p>

<pre><code class="bash">pm2 stop the-sea-serpent
</code></pre></li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Land ho! The maiden voyage was a roaring success. You have successfully taken your application out of the development shipyard and prepared it for the rigors of the open ocean.</p>

<p>You now understand the critical difference between development and production environments and how to manage configurations for both. You have wielded a professional process manager, PM2, to launch, monitor, and control your application like a true fleet commander. While we only simulated this locally, these are the exact skills and commands you would use when deploying to a real server on a host like DigitalOcean or AWS.</p>

<hr />

<h4><strong>Rewards &amp; Promotion</strong></h4>

<ul>
<li><strong>+350 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You have successfully configured and launched your application for a production environment. Your ship is ready to sail the world.

<ul>
<li><strong>Badge Earned:</strong> <code>Seasoned Mariner</code> 🌊</li>
</ul></li>
<li><strong>Promotion to: Captain</strong></li>
</ul>

<p>Take the helm, Captain. You have proven your mastery of this vessel, from its initial construction to its final launch. There is only one challenge remaining: to command a fleet of your own.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Learn Sails.js - Part 8]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/learn-sails-js-part-8" />
      <id>tag:https:2025:/next-direction.de/2.40</id>
      <published>2025-10-12T10:00:00Z</published>
      <updated>2025-08-17T18:32:07Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-8.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-8.jpg" alt="part-8.jpg"/><br>
        <p>Posting guards is only half the battle. A truly professional crew constantly runs drills to ensure every system is in perfect working order before the storm hits. It&#8217;s time to build your safety net.</p>

<hr />

<h3><strong>Part 8: Running Ship Drills (Automated Testing)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Quartermaster</li>
<li><strong>Objective:</strong> To write automated tests for your application, ensuring code quality, preventing regressions, and building a safety net that allows you to make changes with confidence.</li>
</ul>

<hr />

<h4><strong>The Captain&#8217;s Briefing (Background and Theory)</strong></h4>

<p>Our ship is complex now. It has a routing system, a view layer, a database interface, authentication guards, and specialist services. If you change a single line of code in the <code>AppraisalService</code>, how can you be certain you didn&#8217;t accidentally break the login system? Do you manually click through every single feature of the entire ship after every single change?</p>

<p>That is the path to madness. A professional crew runs <strong>drills</strong>. An automated test is a drill for your code. It&#8217;s a script you write that checks if a piece of your application behaves exactly as you expect. You can run thousands of these drills in seconds, giving you immediate feedback.</p>

<p><strong>The Drill Sergeant: Mocha</strong></p>

<p>Sails.js comes equipped with a battle-hardened testing framework called <strong>Mocha</strong> right out of the box. Mocha provides a simple structure for organizing your tests:</p>

<ul>
<li><code>describe('the system being tested', ...)</code>: This creates a &#8220;test suite,&#8221; a collection of related drills. For example, <code>describe('AppraisalService', ...)</code></li>
<li><code>it('should do this specific thing', ...)</code>: This is the individual drill or &#8220;test case.&#8221; Its description should read like a sentence explaining what should happen. For example, <code>it('should calculate the correct tax', ...)</code></li>
<li><strong>Assertions</strong>: Inside the <code>it()</code> block, you perform an action and then <em>assert</em> that the outcome is what you expected. An assertion is a simple check that throws an error if the condition is false. For example: <code>assert.equal(actualValue, expectedValue)</code>. If they aren&#8217;t equal, the test fails.</li>
</ul>

<p><strong>The Test Environment</strong></p>

<p>Crucially, you never want your drills to interfere with your real cargo. <a href="https://blog.sailscasts.com/testing-sails-applications-with-mocha-and-supertest">blog.sailscasts.com</a> explains that Sails is configured to use a special <strong>test environment</strong> when you run your tests. As part of this setup, it&#8217;s common practice to use an in-memory or separate test database, ensuring your tests run in a clean, isolated environment and don&#8217;t touch your development data. Good tests clean up after themselves, a principle mentioned in the Sails docs for effective testing <a href="https://app.studyraid.com/en/read/15017/519331/using-test-fixtures-and-mocks-effectively">app.studyraid.com</a>.</p>

<p><strong>The Bootstrap File: Hoist the Sails!</strong></p>

<p>To test any part of Sails, the app itself needs to be running. Sails provides a special file, <code>test/bootstrap.test.js</code>, for this purpose. Before any of your drills are run, this bootstrap file runs <code>sails.lift()</code> to start the app in the test environment. After all your drills are complete, it runs <code>sails.lower()</code> to shut it down cleanly. You don&#8217;t need to touch this file, but it&#8217;s important to know it&#8217;s working for you behind the scenes.</p>

<p>Today, we will write a <strong>Unit Test</strong>. A unit test focuses on the smallest possible &#8220;unit&#8221; of code—in our case, a single service function—in complete isolation.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>Automated Test</code>: A script that verifies the correctness of your application code.</li>
<li><code>Mocha</code>: The JavaScript testing framework that comes with Sails.</li>
<li><code>describe()</code>: Defines a test suite.</li>
<li><code>it()</code>: Defines a single test case.</li>
<li><code>Assertion</code>: A statement that checks if a condition is true (e.g., <code>assert.equal()</code>).</li>
<li><code>Unit Test</code>: A test for a small, isolated piece of functionality.</li>
<li><code>Test Environment</code>: A separate configuration used for running tests to prevent side effects.</li>
<li><code>npm test</code>: The universal command to run all automated tests in a project.</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;The Appraiser&#8217;s Examination&#8221;</strong></h4>

<p>We hired a new Appraiser in part 5, but how do we know they are trustworthy? We will write a formal examination (a unit test) to verify that the <code>AppraisalService</code> is, and always will be, calculating taxes correctly.</p>

<ul>
<li><p><strong>Task 1: Prepare the Examination Room</strong></p>

<ol>
<li>Test files live in the <code>test/</code> directory. By convention, we&#8217;ll create a folder for our unit tests.</li>
<li>In your code editor, create a new folder named <code>unit</code> inside the <code>test/</code> directory.</li>
<li>Inside <code>test/unit/</code>, create a new file named <code>AppraisalService.test.js</code>.</li>
</ol></li>
<li><p><strong>Task 2: Write the Examination Questions</strong></p>

<ol>
<li><p>Open <code>test/unit/AppraisalService.test.js</code> and add the following test code. This is a classic Mocha test suite.</p>

<pre><code class="javascript">const assert = require('assert');

// We are testing our AppraisalService
describe('AppraisalService', function() &#123;

  // This is the specific test case
  it('should correctly calculate the 15% Crown\'s Tax on a value', function() &#123;

    // Step 1: Arrange - Set up our inputs and expected outputs
    const originalValue = 1000;
    const expectedTaxedValue = 850; // 1000 - 15%

    // Step 2: Act - Run the code we are testing
    const actualTaxedValue = AppraisalService.getTaxedValue(originalValue);

    // Step 3: Assert - Check if the result is what we expected
    assert.equal(actualTaxedValue, expectedTaxedValue, 'The calculated tax was not correct.');

  &#125;);

  it('should handle a value of zero', function() &#123;
    assert.equal(AppraisalService.getTaxedValue(0), 0);
  &#125;);

&#125;);
</code></pre></li>
</ol></li>
<li><p><strong>Task 3: Run the Drill</strong></p>

<ol>
<li>You run tests from the command line using the <code>npm test</code> command. This command is pre-configured in your <code>package.json</code> file by Sails.</li>
<li><p>In your terminal, inside the project root, run:</p>

<pre><code class="bash">npm test
</code></pre></li>
<li><p>You should see output from Mocha showing your tests running. If all is well, it will end with a satisfying green message:</p>

<pre><code>  AppraisalService
    ✓ should correctly calculate the 15% Crown's Tax on a value
    ✓ should handle a value of zero


  2 passing (20ms)
</code></pre>

<p>This confirms your <code>AppraisalService</code> is working as expected.</p></li>
</ol></li>
<li><p><strong>Task 4: Witness a Failed Drill (The Safety Net in Action)</strong></p>

<ol>
<li>Now, let&#8217;s see what happens when we break something. This is the most important part of testing.</li>
<li>Open your service file: <code>api/services/AppraisalService.js</code>.</li>
<li><p>Temporarily change the tax rate. Let&#8217;s say the crown gets greedy. Change <code>0.15</code> to <code>0.20</code>.</p>

<pre><code class="javascript">// In AppraisalService.js
const CROWN_TAX_RATE = 0.20; // Changed from 0.15
</code></pre></li>
<li><p>Save the file and run the drill again from your terminal:</p>

<pre><code class="bash">npm test
</code></pre></li>
<li><p>This time, the test will <strong>fail</strong>. Mocha will give you a detailed, bright red report showing you exactly what went wrong.</p>

<pre><code>  AppraisalService
    1) should correctly calculate the 15% Crown's Tax on a value
    ...

  1 passing (21ms)
  1 failing

  1) AppraisalService
       should correctly calculate the 15% Crown's Tax on a value:

      AssertionError &#91;ERR_ASSERTION&#93;: The calculated tax was not correct.
      + expected - actual

      - 800
      + 850
</code></pre>

<p>Your test just acted as your safety net! It caught a bug (a &#8220;regression&#8221;) automatically. You now know precisely what you broke.</p></li>
<li>Go back to <code>AppraisalService.js</code>, change the rate back to <code>0.15</code>, and run <code>npm test</code> one more time to see everything pass.</li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Flawless execution! The drill was a success. You have now established a fundamental process for ensuring the quality and stability of your ship.</p>

<p>You&#8217;ve created a unit test that verifies your business logic, and more importantly, you&#8217;ve seen how that test protects you from introducing new bugs down the line. As your application grows, your test suite will become your most trusted advisor, giving you the confidence to refactor code, add new features, and sail your application into new waters without fear of capsizing. You can even integrate tools like Istanbul to measure your test coverage, as noted by Packt <a href="https://www.packtpub.com/en-us/learning/how-to-tutorials/how-add-unit-tests-sails-framework-application?srsltid=AfmBOorKhZ8KM8kDNBHCPss6_DtBqlQNYdBpAHvu6o9exkvTbZumhqlE">packtpub.com</a>.</p>

<hr />

<h4><strong>Rewards &amp; Promotion</strong></h4>

<ul>
<li><strong>+300 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You have built an automated safety net to protect your application from regressions.

<ul>
<li><strong>Badge Earned:</strong> <code>Shipshape Scrutineer</code> 🔬</li>
</ul></li>
<li><strong>Promotion to: First Mate</strong></li>
</ul>

<p>Incredible work. As First Mate, you are now second-in-command. You have mastered the ship&#8217;s internal workings, its crew, its defenses, and its procedures. There is only one task left: to prepare this vessel for its maiden voyage into the wider world.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Learn Sails.js - Part 7]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/learn-sails-js-part-7" />
      <id>tag:https:2025:/next-direction.de/2.39</id>
      <published>2025-10-05T10:00:00Z</published>
      <updated>2025-08-17T18:30:50Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-7.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-7.jpg" alt="part-7.jpg"/><br>
        <p>The locks and coffers are in your charge. A ship carrying valuable cargo in open waters without guards is not a warship; it&#8217;s a target. It&#8217;s time to post the watch and secure this vessel.</p>

<hr />

<h3><strong>Part 7: Guarding the Gangway (Authentication &amp; Policies)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Quartermaster</li>
<li><strong>Objective:</strong> To secure your application by implementing a robust user authentication system (login/logout) and using Policies to enforce access control rules.</li>
</ul>

<hr />

<h4><strong>The Captain&#8217;s Briefing (Background and Theory)</strong></h4>

<p>So far, our ship has an open-gangway policy. Anyone can wander aboard, read the Captain&#8217;s Log (<code>/logbook</code>), and even create new treasure using our API. This is untenable. We need to distinguish between an anonymous visitor and a verified member of the crew. This process involves two distinct concepts: <strong>Authentication</strong> and <strong>Authorization</strong>.</p>

<p><strong>Authentication: &#8220;Show Me Your Papers&#8221;</strong></p>

<p>Authentication is the process of verifying who someone is. On the web, this is the classic login form. The user provides credentials (like a name and password), and the server checks if they are valid.</p>

<p>How does the server remember the user after they log in? Through a <strong>Session</strong>. When a user logs in successfully, the server gives their browser a special, secret cookie that acts like a temporary crew pass. On every subsequent request, the browser sends this cookie back, and the server uses it to retrieve that user&#8217;s session data (e.g., their user ID), confirming they are still &#8220;logged in.&#8221;</p>

<p>A critical part of authentication is <strong>securely storing passwords</strong>. You must <strong>never, ever</strong> store plain-text passwords in your database. We will use a one-way cryptographic hashing algorithm (<code>bcrypt</code>) to turn passwords into a long, irreversible string of characters. When a user tries to log in, we hash the password they provided and compare it to the hash in our database. The passwords are never stored in a readable format.</p>

<p><strong>Authorization with Policies: &#8220;Are You on the List?&#8221;</strong></p>

<p>Authorization happens <em>after</em> authentication. Now that we know who the user is, we must decide what they are <em>allowed to do</em>. An Able Seaman can access the mess hall, but only the Quartermaster can access the cargo manifest.</p>

<p>In Sails, authorization is handled by <strong>Policies</strong>. A Policy is a small middleware function that you place in <code>api/policies/</code>. It&#8217;s a guard who stands at the gangway of a specific controller action. The guard checks the request (e.g., &#8220;Does this person have a valid session ID?&#8221;) and decides to either:
1.  Let them pass by calling <code>next()</code>.
2.  Deny them access by sending a response like <code>res.forbidden()</code>.</p>

<p>You assign these guards to their posts in the <code>config/policies.js</code> file. <a href="https://sailsjs.com/documentation/concepts/policies">sailsjs.com</a> explains that this file acts as your application&#8217;s master Access Control List (ACL). You create a map that says, &#8220;To access <em>this</em> action, you must first pass through <em>these</em> policies.&#8221; This provides a powerful, centralized place to manage your entire application&#8217;s security rules. <a href="https://blog.sailscasts.com/understanding-sails-policies-and-best-practices">blog.sailscasts.com</a> highlights the most common and powerful pattern: deny access to everything by default, then explicitly grant access to specific actions.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>Authentication</code>: Verifying a user&#8217;s identity (e.g., login).</li>
<li><code>Authorization</code>: Determining what an authenticated user is permitted to do.</li>
<li><code>Session</code>: Server-side data linked to a browser cookie, used to &#8220;remember&#8221; a logged-in user.</li>
<li><code>Password Hashing</code>: The non-reversible process of scrambling a password for secure storage. We&#8217;ll use the <code>bcryptjs</code> library.</li>
<li><code>Policy</code>: A middleware function in <code>api/policies/</code> that guards a route.</li>
<li><code>config/policies.js</code>: The file where you assign policies to controllers and actions.</li>
<li><code>req.session</code>: The object where you can store data for the current user&#8217;s session.</li>
<li><code>sails generate policy</code>: The command to create a new policy file.</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;Secure the Quarterdeck&#8221;</strong></h4>

<p>We will now lock down the Captain&#8217;s Log (<code>/logbook</code>). It will only be accessible to a logged-in Captain. We will create signup, login, and logout functions to manage this access.</p>

<ul>
<li><p><strong>Task 1: Prepare for Secure Passwords</strong></p>

<ol>
<li><p>We need the <code>bcryptjs</code> library. From your terminal, inside your project folder, run:</p>

<pre><code class="bash">npm install bcryptjs --save
</code></pre></li>
<li><p>Update your <code>Captain</code> model to store a secure password. Open <code>api/models/Captain.js</code> and add the <code>encryptedPassword</code> attribute.</p>

<pre><code class="javascript">module.exports = &#123;
  attributes: &#123;
    name: &#123; type: 'string', required: true, unique: true &#125;,
    rank: &#123; type: 'string', defaultsTo: 'Captain' &#125;,
    encryptedPassword: &#123; type: 'string', required: true &#125;,

    treasures: &#123;
      collection: 'treasure',
      via: 'owner'
    &#125;
  &#125;
&#125;;
</code></pre></li>
</ol></li>
<li><p><strong>Task 2: Assemble the Guard (Create the Policy)</strong></p>

<ol>
<li><p>Generate the policy file:</p>

<pre><code class="bash">sails generate policy isLoggedIn
</code></pre></li>
<li><p>Open <code>api/policies/isLoggedIn.js</code> and add this logic. This guard checks for a valid session.</p>

<pre><code class="javascript">module.exports = async function (req, res, next) &#123;
  // If the user isn't logged in...
  if (!req.session.userId) &#123;
    // ...then forbid access and stop.
    return res.forbidden('You are not logged in.');
  &#125;
  // Otherwise, if they are logged in, continue to the requested action.
  return next();
&#125;;
</code></pre></li>
</ol></li>
<li><p><strong>Task 3: Post the Guard Duty Roster (Configure Policies)</strong></p>

<ol>
<li>Open <code>config/policies.js</code>. This is where we tell our ship which areas need guarding.</li>
<li><p>Replace the entire contents with this &#8220;default deny&#8221; configuration. We lock everything down, then open up only the specific routes needed for authentication.</p>

<pre><code class="javascript">module.exports.policies = &#123;
  // By default, require users to be logged in to access any action.
  '*': 'isLoggedIn',

  // Allow visitors to access the login, signup, and logout actions
  // in the AuthController.
  AuthController: &#123;
    'login': true,
    'signup': true,
    'logout': true
  &#125;,

  // The default homepage should be accessible to all.
  // We target the specific route action for the homepage.
  'view-pages-homepage': true
&#125;;
</code></pre></li>
</ol></li>
<li><p><strong>Task 4: Build the Authentication System</strong></p>

<ol>
<li>Generate a new controller to handle login/logout/signup: <code>sails generate controller AuthController</code>.</li>
<li><p>Open <code>api/controllers/AuthController.js</code> and fill it with these actions:</p>

<pre><code class="javascript">const bcrypt = require('bcryptjs');

module.exports = &#123;

  signup: async function (req, res) &#123;
    const &#123; name, password &#125; = req.allParams();
    if (!name || !password) &#123;
      return res.badRequest('Name and password are required.');
    &#125;

    const encryptedPassword = await bcrypt.hash(password, 10);
    const captain = await Captain.create(&#123; name, encryptedPassword &#125;).fetch();

    // Log the user in immediately after signup
    req.session.userId = captain.id;

    return res.ok(captain);
  &#125;,

  login: async function (req, res) &#123;
    const &#123; name, password &#125; = req.allParams();
    if (!name || !password) &#123;
      return res.badRequest('Name and password are required.');
    &#125;

    const captain = await Captain.findOne(&#123; name &#125;);
    if (!captain) &#123;
      return res.unauthorized('Invalid credentials.');
    &#125;

    const isPasswordMatch = await bcrypt.compare(password, captain.encryptedPassword);
    if (!isPasswordMatch) &#123;
      return res.unauthorized('Invalid credentials.');
    &#125;

    // If we made it here, the credentials are valid. Log the user in.
    req.session.userId = captain.id;

    return res.ok('Login successful.');
  &#125;,

  logout: async function (req, res) &#123;
    req.session.destroy(function (err) &#123;
      if (err) &#123; return res.serverError(err); &#125;
      return res.ok('Logout successful.');
    &#125;);
  &#125;

&#125;;
</code></pre></li>
</ol></li>
<li><p><strong>Task 5: Chart the Authentication Routes</strong></p>

<ol>
<li><p>Open <code>config/routes.js</code> and add the new routes, preferably near the top. We use <code>POST</code> as it&#8217;s the correct verb for sending data to create sessions or users.</p>

<pre><code class="javascript">'POST /signup': 'AuthController.signup',
'POST /login': 'AuthController.login',
'GET /logout': 'AuthController.logout',
</code></pre></li>
</ol></li>
<li><p><strong>Task 6: Test Your Security</strong></p>

<ol>
<li>Restart your server with <code>sails lift</code>.</li>
<li><strong>In your browser</strong>, first try to access <code>http://localhost:1337/logbook</code>. You will be blocked! The <code>isLoggedIn</code> policy is working.</li>
<li>This time, we need to use <code>curl</code> or Postman to test the <code>POST</code> routes properly. From a <strong>new terminal</strong>:</li>
<li><p><strong>Sign up a new Captain:</strong></p>

<pre><code class="bash">curl -X POST http://localhost:1337/signup --data "name=AnneBonny&amp;password=secretpassword" -c cookies.txt
</code></pre>

<p>The <code>-c cookies.txt</code> command saves the session cookie from the server.</p></li>
<li><p><strong>Attempt to access the logbook with your credentials:</strong></p>

<pre><code class="bash">curl http://localhost:1337/logbook -b cookies.txt
</code></pre>

<p>Success! Because you sent the cookie back (<code>-b cookies.txt</code>), the server knew who you were and the policy let you pass. You should see the HTML for the logbook page.</p></li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Excellent work, Quartermaster. The gangway is secure. The ship is no longer open to just any port visitor.</p>

<p>You have successfully implemented a professional-grade authentication and authorization system. You learned how to securely handle passwords, manage user sessions, and use policies as intelligent guards for your application&#8217;s sensitive areas. By adopting the &#8220;default deny&#8221; strategy in <code>config/policies.js</code>, you have established a robust security posture that will serve you well as your application grows in complexity. The ship and its cargo are now safe under your watch.</p>

<hr />

<h4><strong>Rewards</strong></h4>

<ul>
<li><strong>+300 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You have mastered the arts of authentication and authorization, securing the ship from unauthorized access.

<ul>
<li><strong>Badge Earned:</strong> <code>The Gatekeeper</code> 🔑</li>
</ul></li>
</ul>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Learn Sails.js - Part 6]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/learn-sails-js-part-6" />
      <id>tag:https:2025:/next-direction.de/2.38</id>
      <published>2025-09-28T10:00:00Z</published>
      <updated>2025-08-17T18:28:52Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-6.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-6.jpg" alt="part-6.jpg"/><br>
        <p>Excellent. The crew is organized, but our communication methods are antiquated. Sending a message to another ship requires a fast sloop and a fair wind. What if we could send messages instantly, as if by magic? It&#8217;s time to install the ship&#8217;s most advanced piece of equipment.</p>

<hr />

<h3><strong>Part 6: Instant Dispatches (Real-time with WebSockets)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Navigator</li>
<li><strong>Objective:</strong> To master the signature feature of Sails.js by upgrading your application with a real-time WebSocket layer for instantaneous, two-way communication.</li>
</ul>

<hr />

<h4><strong>The Captain&#8217;s Briefing (Background and Theory)</strong></h4>

<p>Navigator, thus far all our communication has followed the &#8220;message in a bottle&#8221; protocol (standard HTTP). We send a request from our browser, and some time later, the server sends a response back. The server can <em>never</em> speak first. If a new treasure is discovered somewhere in the world, our ship won&#8217;t know about it until we manually ask for an update by refreshing the page.</p>

<p>This changes today. We are installing a <strong>Magical Telegraph</strong>, a technology known in other worlds as <strong>WebSockets</strong>.</p>

<p>A WebSocket isn&#8217;t a single message; it&#8217;s an open, persistent, two-way communications channel between the browser and your Sails server. It&#8217;s like having a direct, magical link. Your server can now push information to the browser at any time, without being asked. This is the foundation of all modern real-time features: live chats, notifications, activity feeds, and collaborative applications.</p>

<p><strong>Sails.js and Socket.io: The Telegraph Machine</strong></p>

<p>Setting up WebSockets from scratch is a complex feat of engineering. Thankfully, Sails comes with a masterfully built telegraph machine called <strong>Socket.io</strong> integrated directly into its core. Sails provides a set of simple, elegant commands to manage this powerful technology.</p>

<p><strong>Subscribing to a Channel (The Room)</strong></p>

<p>The key to managing real-time communication is organization. You don&#8217;t want every message going to every person. You create channels, or &#8220;rooms.&#8221; A socket (a single browser connection) can be made to <strong>join</strong> a room.
<code>sails.sockets.join(req, 'some-room-name');</code>
This command tells Sails, &#8220;The browser that made this request is now listening to the &#8216;some-room-name&#8217; channel.&#8221;</p>

<p><strong>Broadcasting a Message</strong></p>

<p>Once sockets have joined a room, you can send a message specifically to that room from your server-side code:
<code>sails.sockets.broadcast('some-room-name', 'eventName', &#123; some: 'data' &#125;);</code>
This sends a message with the name <code>eventName</code> and a data payload to <em>every single socket</em> listening to that channel.</p>

<p><strong>Blueprint Pub/Sub: The Auto-Magic</strong></p>

<p>Here is where Sails ascends from useful to truly magical. This concept is called Publish/Subscribe, or &#8220;Pub/Sub.&#8221;
When you use the Blueprint API from a browser over a socket connection, Sails automatically manages the room subscriptions for you!</p>

<ul>
<li>When you make a request like <code>io.socket.get('/treasure', ...)</code>, Sails sees this and thinks, &#8220;Aha! This browser is interested in treasure.&#8221; It automatically subscribes that socket to notifications about the <code>Treasure</code> model.</li>
<li>Later, if anyone in the world (via another browser or a script) <strong>creates</strong>, <strong>updates</strong>, or <strong>destroys</strong> a treasure, the Blueprint API will <strong>publish</strong> an event.</li>
<li>Sails automatically broadcasts this event to all subscribed sockets.</li>
</ul>

<p>This means you get real-time model updates for free, just by using the API you already know over a socket connection.</p>

<p><strong>Listening on the Client</strong></p>

<p>How does the browser listen for these incoming telegraph messages? Sails provides a client-side JavaScript file, <code>sails.io.js</code>, which is included for you automatically. It gives you a global <code>io</code> object. The most important function is <code>io.socket.on()</code>:</p>

<pre><code class="javascript">// On the front-end, in a &lt;script&gt; tag
io.socket.on('eventName', function (data) &#123;
  // This code runs when a message called 'eventName' arrives
  console.log('The server sent a message!', data);
&#125;);
</code></pre>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>WebSockets</code>: A technology for persistent, two-way communication between server and client.</li>
<li><code>Socket.io</code>: The library Sails uses to manage WebSockets.</li>
<li><code>sails.io.js</code>: The client-side JavaScript library for connecting to the Sails socket server.</li>
<li><code>Subscription</code>: The act of putting a socket into a &#8220;room&#8221; to listen for specific messages.</li>
<li><code>Blueprint Pub/Sub</code>: The powerful &#8220;publish/subscribe&#8221; feature that automatically broadcasts model changes to subscribed sockets.</li>
<li><code>io.socket.get()</code>: Making a <code>GET</code> request over a socket instead of standard HTTP.</li>
<li><code>io.socket.on()</code>: The client-side listener for incoming socket events.</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;The Live Treasure Tracker&#8221;</strong></h4>

<p>We will build a simple dashboard that displays a list of all treasures. Then, using the magic of WebSockets, we will make it so that when a new treasure is created <em>anywhere</em>, it instantly appears on our dashboard page without a refresh.</p>

<ul>
<li><p><strong>Task 1: Build the Dashboard Page</strong></p>

<ol>
<li>Create a new controller with the generator: <code>sails generate controller TreasureController</code></li>
<li><p>Open <code>api/controllers/TreasureController.js</code> and add an action to just show a page:</p>

<pre><code class="javascript">module.exports = &#123;
  trackerPage: function (req, res) &#123;
    return res.view('pages/tracker');
  &#125;
&#125;;
</code></pre></li>
<li><p>Create the corresponding view file at <code>views/pages/tracker.ejs</code>:</p>

<pre><code class="html">&lt;h1&gt;Live Treasure Fleet Tracker&lt;/h1&gt;
&lt;p&gt;This list will update in real-time as new treasures are added to the database.&lt;/p&gt;
&lt;ul id="treasure-list"&gt;
  &lt;!-- Treasures will be added here by our script --&gt;
&lt;/ul&gt;

&lt;!-- Add a script block at the bottom for our client-side code --&gt;
&lt;script src="/js/dependencies/sails.io.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
  // We will fill this in next
&lt;/script&gt;
</code></pre></li>
<li>Add the route in <code>config/routes.js</code>: <code>'GET /tracker': 'TreasureController.trackerPage'</code></li>
</ol></li>
<li><p><strong>Task 2: Write the Client-Side Telegraph Operator</strong></p>

<ol>
<li><p>In your <code>views/pages/tracker.ejs</code> file, replace the empty script body with this JavaScript code. Read the comments carefully to understand the steps.</p>

<pre><code class="javascript">const treasureList = document.getElementById('treasure-list');

// Helper function to add a treasure to our &lt;ul&gt; list
function addTreasureToList(treasure) &#123;
  const newListItem = document.createElement('li');
  newListItem.textContent = `$&#123;treasure.name&#125; (Value: $&#123;treasure.value&#125; doubloons)`;
  treasureList.appendChild(newListItem);
&#125;

// --- Step 1: Get the initial list of treasures when the page loads ---
// We use io.socket.get() to make a request over a WebSocket connection.
// This automatically subscribes this browser to all "treasure" model events!
io.socket.get('/treasure', function (treasures, JWR) &#123;
  if (JWR.statusCode === 200) &#123;
    treasures.forEach(addTreasureToList);
  &#125; else &#123;
    console.error('Could not fetch treasures from server');
  &#125;
&#125;);

// --- Step 2: Listen for real-time updates from the server ---
// The event name 'treasure' is automatically chosen by Sails based on the model name.
io.socket.on('treasure', function (event) &#123;
  // The 'event' object tells us what happened.
  // We only care about newly created treasures for this quest.
  if (event.verb === 'created') &#123;
    const newTreasure = event.data;
    addTreasureToList(newTreasure);
    console.log('A new treasure appeared by magic!', newTreasure);
  &#125;
&#125;);
</code></pre></li>
</ol></li>
<li><p><strong>Task 3: Witness the Magic</strong></p>

<ol>
<li>Restart your server with <code>sails lift</code>.</li>
<li><strong>Open Browser Tab 1:</strong> Navigate to <code>http://localhost:1337/tracker</code>. You should see a list of any treasures you created in Module 4.</li>
<li><strong>Open Browser Tab 2:</strong> We need to create a new treasure. Use the Blueprint API <code>create</code> route. Assuming you have a captain with <code>id</code> of <code>1</code>, navigate to this URL:
<code>http://localhost:1337/treasure/create?name=The Kraken's Eye&amp;value=99000&amp;owner=1</code></li>
<li>As soon as you press Enter in Tab 2, look at Tab 1. <strong>Instantly</strong>, without refreshing the page, &#8220;The Kraken&#8217;s Eye&#8221; will appear at the bottom of the list. You have just witnessed a real-time WebSocket push from the server!</li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Incredible! The magical telegraph is online. You have achieved one of the most powerful and sought-after features in modern web development.</p>

<p>You created a page that subscribed itself to model updates just by fetching its initial data over a socket. When another user (you, in another tab) modified the data, the Sails backend <strong>published</strong> an event, which was instantly pushed to your tracker page. Your client-side JavaScript caught the event and updated the display. This entire complex workflow was handled with just a few lines of code, demonstrating the immense power and productivity of the Sails.js framework.</p>

<hr />

<h4><strong>Rewards &amp; Promotion</strong></h4>

<ul>
<li><strong>+250 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You have bent the laws of space and time, sending messages instantly from server to client. You are a master of real-time communication.

<ul>
<li><strong>Badge Earned:</strong> <code>Socket Sorcerer</code> ✨</li>
</ul></li>
<li><strong>Promotion to: Quartermaster</strong></li>
</ul>

<p>As Quartermaster, you are a master of the ship&#8217;s stores (Models) and now its communications (Sockets). You hold a position of immense trust and authority. Your next duty is to ensure the security of the ship.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Learn Sails.js - Part 5]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/learn-sails-js-part-5" />
      <id>tag:https:2025:/next-direction.de/2.37</id>
      <published>2025-09-21T10:00:00Z</published>
      <updated>2025-08-17T18:27:25Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-5.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-5.jpg" alt="part-5.jpg"/><br>
        <p>Excellent. The ship is seaworthy, the holds are filling with data, and you know how to steer. But a complex voyage requires more than just a single captain on the bridge shouting orders. It requires a specialist crew. It&#8217;s time to hire some experts.</p>

<hr />

<h3><strong>Part 5: Assembling a Specialist Crew (Services)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Navigator</li>
<li><strong>Objective:</strong> To learn how to organize and reuse your core application logic by creating Services, leading to cleaner, more maintainable, and more powerful code.</li>
</ul>

<hr />

<h4><strong>The Captain&#8217;s Briefing (Background and Theory)</strong></h4>

<p>Navigator, as your voyages become more ambitious, your Captain&#8217;s Log (the <code>CaptainController.js</code> file) will start to get cluttered. Imagine you need to perform a complex calculation, like determining the total value of a captain&#8217;s treasure, adjusted for inflation, tides, and the current price of rum.</p>

<p>Where would you put that code?</p>

<p>You <em>could</em> write a huge, messy function directly inside a controller action. But what if you need to perform that <em>same exact calculation</em> somewhere else, like in a different controller or in a script? You&#8217;d have to copy and paste the code. This is a cardinal sin in programming, a violation of the <strong>DRY (Don&#8217;t Repeat Yourself)</strong> principle. Copied code is twice as hard to maintain and twice as likely to have bugs.</p>

<p>This is the problem <strong>Services</strong> are designed to solve.</p>

<p><strong>Services: The Specialist Crew</strong></p>

<p>A Service is a dedicated specialist on your ship. It&#8217;s a file that lives in <code>api/services/</code> and contains a collection of related helper functions. Think of them by their specialty:</p>

<ul>
<li><code>EmailService.js</code>: The ship&#8217;s messenger, responsible for sending all types of emails.</li>
<li><code>PaymentService.js</code>: The ship&#8217;s purser, who knows how to handle all monetary transactions.</li>
<li><code>ImageManipulationService.js</code>: The ship&#8217;s artist, skilled in resizing and watermarking images.</li>
<li><code>GeocodingService.js</code>: Your master cartographer, who can turn a location name into latitude and longitude.</li>
</ul>

<p>A controller&#8217;s job is to be the <strong>manager</strong>. It should be lean and decisive. It receives a request, calls on the appropriate specialists (models and services) to do the heavy lifting, and then decides what response to send. The controller gives the <em>order</em>; the service <em>executes the skill</em>.</p>

<p><strong>Using Services</strong></p>

<p>Once you create a service file like <code>AppraisalService.js</code>, Sails makes it globally available throughout your backend code. You don&#8217;t need to <code>require()</code> it. You can simply call its methods from any controller, policy, or even another service by using its global name.</p>

<p>If you have a function called <code>calculateTax</code> in <code>AppraisalService.js</code>, you can call it from anywhere like this:
<code>const taxedValue = await AppraisalService.calculateTax(treasure);</code></p>

<p>This makes your controllers incredibly clean and easy to read. Compare these two styles:</p>

<p><strong>Bloated Controller (Bad):</strong></p>

<pre><code class="javascript">// In a controller action
const treasure = await Treasure.findOne(&#123; id: req.param('id') &#125;);
let taxedValue = treasure.value;
// ... 20 lines of complex tax calculation logic here ...
// ... what if the tax rules change? You have to find this code and change it.
return res.json(&#123; taxedValue: taxedValue &#125;);
</code></pre>

<p><strong>Clean Controller with Service (Good):</strong></p>

<pre><code class="javascript">// In a controller action
const treasure = await Treasure.findOne(&#123; id: req.param('id') &#125;);
// Call the specialist crew member!
const taxedValue = await AppraisalService.calculateTax(treasure);
return res.json(&#123; taxedValue: taxedValue &#125;);
</code></pre>

<p>The logic is now neatly encapsulated in one place (<code>AppraisalService.js</code>), ready to be reused and easy to update.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>Service</code>: A reusable module of helper functions for your core logic (<code>api/services/</code>).</li>
<li><code>DRY Principle</code>: &#8220;Don&#8217;t Repeat Yourself.&#8221; The fundamental concept of not duplicating code.</li>
<li><code>Encapsulation</code>: The practice of bundling related logic into a single, self-contained unit.</li>
<li><code>Business Logic</code>: The core rules and processes that are unique to your application&#8217;s domain (e.g., how you calculate value, process an order, etc.). This is what belongs in services.</li>
<li><code>sails.helpers</code> vs <code>sails.services</code>: While similar, services are traditionally seen as higher-level, stateless &#8220;buckets&#8221; of functions, while helpers are for smaller, granular tasks. For our purposes, services are the perfect place for our core &#8220;business logic.&#8221;</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;Hire an Appraiser&#8221;</strong></h4>

<p>A new tax has been levied on all discovered treasure. We need to hire an Appraiser to calculate the value of a treasure after this new &#8220;Crown&#8217;s Tax&#8221; is applied.</p>

<ul>
<li><p><strong>Task 1: Recruit the Specialist (Create the Service File)</strong></p>

<ol>
<li>This is one of the few things there isn&#8217;t a generator for. We create it manually.</li>
<li>In your code editor, create a new folder named <code>services</code> inside the <code>api/</code> directory.</li>
<li>Inside <code>api/services/</code>, create a new file named <code>AppraisalService.js</code>.</li>
</ol></li>
<li><p><strong>Task 2: Define the Specialist&#8217;s Skill</strong></p>

<ol>
<li><p>Open <code>api/services/AppraisalService.js</code> and add the following code. We&#8217;ll define a simple service with one method.</p>

<pre><code class="javascript">// api/services/AppraisalService.js
module.exports = &#123;

  /**
   * Calculates the value of a treasure after applying the Crown's Tax.
   * @param &#123;number&#125; treasureValue The original value of the treasure.
   * @returns &#123;number&#125; The value after a 15% tax.
   */
  getTaxedValue: function(treasureValue) &#123;
    const CROWN_TAX_RATE = 0.15; // 15%
    const taxAmount = treasureValue * CROWN_TAX_RATE;
    const finalValue = treasureValue - taxAmount;

    // We use Math.round to keep the value clean.
    return Math.round(finalValue);
  &#125;

&#125;;
</code></pre></li>
</ol></li>
<li><p><strong>Task 3: Create a Controller to Use the Service</strong></p>

<ol>
<li>We need an endpoint to test this. Let&#8217;s add a new action to our <code>CaptainController.js</code>. This action will find a treasure and then use our new service to appraise it.</li>
<li><p>Open <code>api/controllers/CaptainController.js</code> and add this new action.</p>

<pre><code class="javascript">module.exports = &#123;
  // ... your existing greet and logbook actions ...

  // Add this new action
  appraiseTreasure: async function(req, res) &#123;
    try &#123;
      // Find the first treasure we have in the database.
      const treasure = await Treasure.findOne();

      if (!treasure) &#123;
        return res.status(404).json(&#123; error: 'No treasure found to appraise.' &#125;);
      &#125;

      // Here's the magic! Call the service.
      const taxedValue = AppraisalService.getTaxedValue(treasure.value);

      return res.json(&#123;
        treasureName: treasure.name,
        originalValue: treasure.value,
        taxedValue: taxedValue,
        message: `After the Crown's 15% tax, the value is $&#123;taxedValue&#125; doubloons.`
      &#125;);

    &#125; catch (err) &#123;
      return res.serverError(err);
    &#125;
  &#125;
&#125;;
</code></pre>

<p><em>Notice the use of <code>async</code> and <code>await</code>. This is modern JavaScript for handling asynchronous operations, like fetching from a database. It makes the code much cleaner.</em></p></li>
</ol></li>
<li><p><strong>Task 4: Chart the New Route</strong></p>

<ol>
<li><p>Open <code>config/routes.js</code> and add a route for our new action.</p>

<pre><code class="javascript">// ... previous routes
'GET /logbook': 'CaptainController.logbook',
'GET /appraise': 'CaptainController.appraiseTreasure', // &lt;-- ADD THIS LINE
'/': &#123; view: 'pages/homepage' &#125;,
// ... rest of the file
</code></pre></li>
</ol></li>
<li><p><strong>Task 5: See the Expert at Work</strong></p>

<ol>
<li>Restart your server with <code>sails lift</code>.</li>
<li>Make sure you have at least one treasure in your database (you can create one using the method from Module 4 if needed). For example, a treasure with a value of <code>1000</code>.</li>
<li>Open your browser and navigate to <code>http://localhost:1337/appraise</code>.</li>
<li>You should see a JSON response detailing the original value and the new, lower taxed value (e.g., <code>850</code>).</li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Mission complete, Navigator. Your command structure is now far more robust. You&#8217;ve successfully delegated a complex task to a specialist, keeping your controller clean and your core logic organized and reusable.</p>

<p>By creating the <code>AppraisalService</code>, you&#8217;ve built an asset that can be called from anywhere. If the Crown&#8217;s Tax rate changes from 15% to 20%, you only have to change it in <strong>one place</strong>: the service file. Every part of your application that uses this service will be instantly updated.</p>

<p>This is the path to building large, professional applications. Your ability to organize code is just as important as your ability to write it.</p>

<hr />

<h4><strong>Rewards</strong></h4>

<ul>
<li><strong>+200 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You&#8217;ve successfully abstracted business logic into a reusable service. Your crew is growing more capable!

<ul>
<li><strong>Badge Earned:</strong> <code>Specialist Crew</code> 🔧</li>
</ul></li>
</ul>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Learn Sails.js - Part 4]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/learn-sails-js-part-4" />
      <id>tag:https:2025:/next-direction.de/2.36</id>
      <published>2025-09-14T10:00:00Z</published>
      <updated>2025-08-17T18:25:14Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-4.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-4.jpg" alt="part-4.jpg"/><br>
        <p>Aye, Captain. The ship looks magnificent, but its cargo hold is empty. A ship with no cargo has no purpose. It&#8217;s time to learn how to manage our treasure. This is the most critical part so far.</p>

<hr />

<h3><strong>Part 4: Stocking the Cargo Hold (Models, Associations &amp; Validations)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Boatswain</li>
<li><strong>Objective:</strong> To define, store, retrieve, and protect your application&#8217;s most valuable asset: its data, using Models, the Waterline ORM, and the magical Blueprint API.</li>
</ul>

<hr />

<h4><strong>The Captain&#8217;s Briefing (Background and Theory)</strong></h4>

<p>This module introduces the &#8216;M&#8217; in MVC: the <strong>Model</strong>. The Model is the single most important part of any data-driven application. It&#8217;s not just the data itself; it&#8217;s the <em>definition</em> and <em>rules</em> for the data. It&#8217;s the manifest that dictates what kind of treasure we can store, what it&#8217;s called, and how much it&#8217;s worth.</p>

<p><strong>Waterline: The Universal Quartermaster</strong></p>

<p>In Sails, your interface to the database is an incredibly powerful tool called <strong>Waterline</strong>. Waterline is an <strong>ORM</strong>, which stands for Object-Relational Mapper.</p>

<p>Think of it this way: there are many different types of vaults in the world (databases like PostgreSQL, MySQL, MongoDB, etc.). A normal Quartermaster would need to learn the specific, complex instructions for each one. But Waterline is a <em>universal</em> Quartermaster. You give Waterline simple commands in plain JavaScript (e.g., &#8220;Store this treasure chest&#8221;), and Waterline figures out how to translate that into the correct command for whatever vault (database) you&#8217;re currently using. By default, Sails uses a simple, file-based database called <code>sails-disk</code> which is perfect for development. Thanks to Waterline, you can switch to a powerful production database later without changing a single line of your application code.</p>

<p><strong>Models: The Cargo Manifest</strong></p>

<p>A Model is a file you create in <code>api/models/</code> that serves as a manifest for a single type of data. If we want to store treasure, we&#8217;ll create a <code>Treasure.js</code> model. This file defines the <strong>attributes</strong> of a Treasure—the properties that every piece of treasure must have.</p>

<pre><code class="javascript">// api/models/Treasure.js
module.exports = &#123;
  attributes: &#123;
    name: &#123; type: 'string', required: true &#125;,
    value: &#123; type: 'number' &#125;,
    location: &#123; type: 'string' &#125;
  &#125;
&#125;;
</code></pre>

<p>Here, we&#8217;ve defined that every <code>Treasure</code> record will have a <code>name</code>, a <code>value</code>, and a <code>location</code>.</p>

<p><strong>The Blueprint API: Your Automated Crane System</strong></p>

<p>This is where the magic of Sails truly shines. Once you define a model, Sails automatically equips your ship with a set of fully functional cargo cranes, ready to handle that data. This is the <strong>Blueprint API</strong>.</p>

<p>Without writing a single line of controller code, by simply creating the <code>Treasure.js</code> model file, Sails instantly creates a set of API &#8220;routes&#8221; for you. These routes allow you to perform all the basic <strong>CRUD</strong> operations (Create, Read, Update, Delete) on your treasure:</p>

<ul>
<li><code>POST /treasure</code>: <strong>Create</strong> a new treasure.</li>
<li><code>GET /treasure</code>: <strong>Read</strong> (find) a list of all treasures.</li>
<li><code>GET /treasure/:id</code>: <strong>Read</strong> (find) one specific treasure by its unique <code>id</code>.</li>
<li><code>PATCH /treasure/:id</code>: <strong>Update</strong> a specific treasure.</li>
<li><code>DELETE /treasure/:id</code>: <strong>Delete</strong> a specific treasure.</li>
</ul>

<p><strong>Associations: The Trade Network</strong></p>

<p>Rarely does data exist in isolation. A treasure belongs to a captain. A captain commands a ship. These are <strong>associations</strong>. Waterline makes it simple to define these relationships directly in your models. A <code>one-to-many</code> association, for example, lets one <code>Captain</code> model own many <code>Treasure</code> models.</p>

<p><strong>Validations: Guarding the Vault</strong></p>

<p>You don&#8217;t want someone stocking your hold with lead painted to look like gold. <strong>Validations</strong> are rules you add to your model attributes to ensure data quality. For example, you can require that a treasure&#8217;s <code>name</code> is always present (<code>required: true</code>) or that its <code>value</code> must be a positive number (<code>min: 0</code>). This is your first and most important line of defense against bad data.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>Model</code>: The definition of a data structure (<code>api/models/</code>).</li>
<li><code>Waterline</code>: The ORM that talks to the database for you.</li>
<li><code>sails-disk</code>: The default development database.</li>
<li><code>Attributes</code>: The properties of a model (e.g., <code>name</code>, <code>email</code>).</li>
<li><code>Validations</code>: Rules on attributes to ensure data integrity (e.g., <code>required</code>, <code>isEmail</code>).</li>
<li><code>CRUD</code>: The four basic data operations: Create, Read, Update, Delete.</li>
<li><code>Blueprint API</code>: Automatically generated CRUD routes for your models.</li>
<li><code>Associations</code>: Defining relationships between models (e.g., one-to-many).</li>
<li><code>sails generate model</code>: The CLI command to create a new model file.</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;The Treasure Fleet&#8221;</strong></h4>

<p>It&#8217;s time to build our treasure manifest and establish ownership. We will create <code>Captain</code> and <code>Treasure</code>, link them together, and add rules to protect our hoard.</p>

<ul>
<li><p><strong>Task 1: Sign Up the Captains (Create Captain Model)</strong></p>

<ol>
<li><p>In your terminal (inside the project), run the generator:</p>

<pre><code class="bash">sails generate model Captain
</code></pre></li>
<li><p>Open <code>api/models/Captain.js</code> and define its attributes:</p>

<pre><code class="javascript">module.exports = &#123;
  attributes: &#123;
    name: &#123; type: 'string', required: true, unique: true &#125;,
    rank: &#123; type: 'string', defaultsTo: 'Captain' &#125;,

    // Add this association!
    treasures: &#123;
      collection: 'treasure',
      via: 'owner'
    &#125;
  &#125;
&#125;;
</code></pre>

<p>The <code>treasures</code> attribute sets up the &#8220;one&#8221; side of our one-to-many relationship.</p></li>
</ol></li>
<li><p><strong>Task 2: Define the Booty (Create Treasure Model)</strong></p>

<ol>
<li><p>Run the generator:</p>

<pre><code class="bash">sails generate model Treasure
</code></pre></li>
<li><p>Open <code>api/models/Treasure.js</code> and define its attributes, including validations and the other side of the association:</p>

<pre><code class="javascript">module.exports = &#123;
  attributes: &#123;
    name: &#123; type: 'string', required: true &#125;,
    value: &#123; type: 'number', required: true, min: 1 &#125;,
    location: &#123; type: 'string', defaultsTo: 'Unknown' &#125;,

    // Add this association! This links back to the Captain.
    owner: &#123;
      model: 'captain',
      required: true
    &#125;
  &#125;
&#125;;
</code></pre></li>
</ol></li>
<li><p><strong>Task 3: Stock the Hold (Interact with the Blueprint API)</strong></p>

<ol>
<li>Restart your server with <code>sails lift</code> to load the new models.</li>
<li>We&#8217;ll use <code>curl</code> from a <strong>second terminal window</strong> (or a tool like Postman) to act as our API client.</li>
<li><p><strong>Create a Captain:</strong></p>

<pre><code class="bash">curl -X POST http://localhost:1337/captain -d "name=Redbeard"
</code></pre>

<p>Sails will respond with the new captain object, including its unique <code>id</code> (e.g., <code>&#123;"id":1, ...&#125;</code>). Note this ID!</p></li>
<li><p><strong>Create a Treasure FOR that Captain:</strong> (Replace <code>1</code> with your Captain&#8217;s ID if different).</p>

<pre><code class="bash">curl -X POST http://localhost:1337/treasure -d "name=Cursed Doubloons&amp;value=5000&amp;owner=1"
</code></pre></li>
<li><p><strong>Try to create invalid treasure:</strong> This should fail because of our validation rule (<code>value</code> cannot be 0).</p>

<pre><code class="bash">curl -X POST http://localhost:1337/treasure -d "name=Fool's Gold&amp;value=0&amp;owner=1"
</code></pre>

<p>Sails will respond with an error message, proving our validation is working!</p></li>
<li><strong>Find all captains and their treasure:</strong> Open your browser and navigate to <code>http://localhost:1337/captain?populate=treasures</code>. The <code>?populate=treasures</code> command tells Waterline to also fetch the associated treasures. You will see Captain Redbeard and an array of all the treasure he owns.</li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Outstanding, Boatswain. The cargo hold is no longer empty, and the manifests are in order. More importantly, the vault is secure.</p>

<p>You have now mastered the core of the Sails framework. By simply defining models, you have unlocked a massive amount of functionality. You&#8217;ve seen how Waterline&#8217;s <strong>validations</strong> protect your data integrity and how <strong>associations</strong> create meaningful relationships. You leveraged the <strong>Blueprint API</strong> to perform complex database operations without writing a single line of controller logic.</p>

<p>This is the power of &#8220;convention over configuration.&#8221; Because you followed Sails&#8217; conventions, the framework did the heavy lifting for you.</p>

<hr />

<h4><strong>Rewards &amp; Promotion</strong></h4>

<ul>
<li><strong>+250 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You have defined, created, and protected data using the framework&#8217;s most powerful features. The blueprints are now yours to command.

<ul>
<li><strong>Badge Earned:</strong> <code>Blueprint Architect</code> 🏗️</li>
</ul></li>
<li><strong>Promotion to: Navigator</strong></li>
</ul>

<p>You&#8217;ve earned your new title, Navigator. You can now chart a course and manage the cargo. The ship is truly taking shape. Next, we will organize our crew for more complex missions.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Learn Sails.js - Part 3]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/learn-sails-js-part-3" />
      <id>tag:https:2025:/next-direction.de/2.35</id>
      <published>2025-09-07T10:00:00Z</published>
      <updated>2025-08-17T18:23:35Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-3.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-3.jpg" alt="part-3.jpg"/><br>
        <p>Aye, aye, Boatswain! Let&#8217;s get this ship looking respectable. It&#8217;s time to hoist the banners and polish the quarterdeck.</p>

<hr />

<h3><strong>Part 3: Designing the Quarterdeck (Views &amp; Assets)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Boatswain</li>
<li><strong>Objective:</strong> To learn how to make your application presentable by creating custom web pages and styling them with CSS via Sails&#8217; Asset Pipeline.</li>
</ul>

<hr />

<h4><strong>The Captain&#8217;s Briefing (Background and Theory)</strong></h4>

<p>So far, our ship can respond to hails, but only by shouting back raw data (JSON). That&#8217;s fine for ship-to-ship communication, but it&#8217;s not very welcoming for distinguished visitors who expect to come aboard and see a polished deck. This module is about creating that polished deck: the <strong>View</strong> layer.</p>

<p><strong>From Data to Decks: <code>res.view()</code></strong></p>

<p>In the last module, we used <code>res.json()</code> to send back data. To send back a full HTML page, we use a different command: <code>res.view()</code>. When you call <code>res.view('some-page')</code>, Sails looks inside your <code>views/pages/</code> folder for a file named <code>some-page.ejs</code> and sends it to the user&#8217;s browser as a web page.</p>

<p><strong>EJS: The Magic Parchment</strong></p>

<p>The files inside your <code>views/</code> folder are not plain HTML. They are <code>.ejs</code> files, which stands for <strong>Embedded JavaScript</strong>. Think of EJS as a magic parchment: it looks and feels like normal HTML, but you can embed special tags into it to print dynamic data.</p>

<ul>
<li><code>&lt;%= data %&gt;</code>: This tag is like a magic quill. It takes a variable from your controller and writes its value directly onto the page. For example, if you pass <code>&#123; captainName: 'Grace O'Malley' &#125;</code> from your controller, <code>&lt;%= captainName %&gt;</code> in your view file will become <code>Grace O'Malley</code> in the final HTML.</li>
</ul>

<p><strong>The Master Blueprint: Layouts</strong></p>

<p>Building every single page from scratch would be tedious. You&#8217;d have to copy and paste your navigation bar, header, and footer on every page. Sails solves this with a <strong>layout</strong> file.</p>

<p>Look in <code>views/layouts/layout.ejs</code>. This is your master ship blueprint. It contains the common HTML structure for your entire site (<code>&lt;html&gt;</code>, <code>&lt;head&gt;</code>, <code>&lt;body&gt;</code>, etc.). Notice the special tag somewhere in the middle: <code>&lt;%- body %&gt;</code>.</p>

<p>When you call <code>res.view('pages/homepage')</code>, Sails first renders <code>homepage.ejs</code>, then takes the resulting HTML and injects it into the <code>layout.ejs</code> file right where <code>&lt;%- body %&gt;</code> is. This way, you only need to define your main structure once!</p>

<p><strong>Paint and Flags: The Asset Pipeline</strong></p>

<p>How do you add styling (CSS), front-end interactivity (JavaScript), or images? You place these files into the <code>assets/</code> directory.</p>

<ul>
<li>Stylesheets go in <code>assets/styles/</code>.</li>
<li>Client-side JavaScript goes in <code>assets/js/</code>.</li>
<li>Images go in <code>assets/images/</code>.</li>
</ul>

<p>This isn&#8217;t just a folder for storage. Sails has a powerful <strong>Asset Pipeline</strong>. When you run <code>sails lift</code>, Sails automatically scans these folders. It will compile, combine, and minify these files for you (in production), and most importantly, it will <strong>automatically inject the necessary <code>&lt;link&gt;</code> and <code>&lt;script&gt;</code> tags</strong> into your <code>layout.ejs</code> file. You don&#8217;t need to manually link to your stylesheets; just drop them in the right folder, and Sails handles the rest.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>res.view()</code>: The response method to render a view and send it as an HTML page.</li>
<li><code>views/</code>: The directory where all <code>.ejs</code> template files are stored.</li>
<li><code>EJS (Embedded JavaScript)</code>: The template language for mixing data with HTML.</li>
<li><code>&lt;%= %&gt;</code>: The EJS tag for outputting data into the HTML.</li>
<li><code>Layouts</code>: The master template (<code>views/layouts/layout.ejs</code>) that wraps your individual view files.</li>
<li><code>Asset Pipeline</code>: The system that automatically manages and includes files from the <code>assets/</code> directory.</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;The Captain&#8217;s Log&#8221;</strong></h4>

<p>Our ship needs a proper logbook. We will create a new page at <code>/logbook</code>, display the Captain&#8217;s name dynamically, and give the page some custom styling.</p>

<ul>
<li><p><strong>Task 1: The New Harbor Master&#8217;s Order (Create the Controller Action)</strong></p>

<ol>
<li>Open your <code>api/controllers/CaptainController.js</code> file.</li>
<li><p>Add a new action called <code>logbook</code> below your <code>greet</code> action.</p>

<pre><code class="javascript">module.exports = &#123;

  greet: function(req, res) &#123; 
    /* ... your existing greet action ... */
  &#125;,

  // Add this new action
  logbook: function(req, res) &#123;
    // We're passing an object with data to the view.
    // The view will be able to access `captainName`.
    return res.view('pages/logbook', &#123;
      captainName: 'Blackheart Beatrice'
    &#125;);
  &#125;

&#125;;
</code></pre></li>
</ol></li>
<li><p><strong>Task 2: Crafting the Page (Create the View File)</strong></p>

<ol>
<li>In your code editor, create a new file.</li>
<li>Save it inside the <code>views/pages/</code> directory as <code>logbook.ejs</code>.</li>
<li><p>Add the following content to this file. Notice how we use the <code>&lt;%= %&gt;</code> tag to display the <code>captainName</code> variable we&#8217;re passing from the controller.</p>

<pre><code class="html">&lt;div id="logbook"&gt;
  &lt;h1&gt;Captain's Log&lt;/h1&gt;
  &lt;h2&gt;A Record for Captain &lt;%= captainName %&gt;&lt;/h2&gt;
  &lt;p&gt;
        Stardate: 47634.4. The ship is performing flawlessly. The crew is in high spirits. 
        We have successfully established our first custom route and are now preparing to chart the vast oceans of the web.
  &lt;/p&gt;
&lt;/div&gt;
</code></pre></li>
</ol></li>
<li><p><strong>Task 3: Charting the New Lane (Create the Route)</strong></p>

<ol>
<li>Open <code>config/routes.js</code>.</li>
<li><p>Add a new route to direct traffic from the <code>/logbook</code> URL to your new action.</p>

<pre><code class="javascript">// ... previous routes
'GET /hail': 'CaptainController.greet',
'GET /logbook': 'CaptainController.logbook', // &lt;-- ADD THIS LINE
'/': &#123; view: 'pages/homepage' &#125;,
// ... rest of the file
</code></pre></li>
</ol></li>
<li><p><strong>Task 4: Painting the Cabin (Add Custom CSS)</strong></p>

<ol>
<li>In the <code>assets/styles/</code> directory, create a new file named <code>custom.css</code>.</li>
<li><p>Add the following CSS rule to give your logbook page some style.</p>

<pre><code class="css">body &#123;
  /* A nice, light blue background for our voyage */
  background-color: #f0f8ff; /* AliceBlue */
&#125;

#logbook &#123;
  border: 1px solid #004d99;
  padding: 20px;
  margin: 40px;
  background-color: #fff;
  border-radius: 5px;
&#125;
</code></pre></li>
</ol></li>
<li><p><strong>Task 5: The Grand Tour</strong></p>

<ol>
<li>Start your server with <code>sails lift</code> (or restart it if it was running).</li>
<li>Open your browser and navigate to <code>http://localhost:1337/logbook</code>.</li>
<li>You should see your new logbook page, complete with the captain&#8217;s name &#8220;Blackheart Beatrice&#8221; and the new light-blue background color. The asset pipeline automatically linked your <code>custom.css</code> file for you!</li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Splendid work, Boatswain. The quarterdeck is immaculate. You have successfully separated logic from presentation.</p>

<ul>
<li>Your <strong>Controller</strong> handled the request and decided <em>what</em> data to show.</li>
<li>Your <strong>View</strong> handled the presentation and decided <em>how</em> to show it.</li>
<li>The <strong>Asset Pipeline</strong> handled the styling, linking your CSS automatically.</li>
</ul>

<p>This is the MVC pattern in its full glory. Your application is no longer just a data machine; it&#8217;s a presentable web application. You can now create as many pages as you wish, each with its own-logic and styling.</p>

<hr />

<h4><strong>Rewards</strong></h4>

<ul>
<li><strong>+150 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You&#8217;ve created a styled web page with dynamic data. The ship&#8217;s banners are flying high!

<ul>
<li><strong>Badge Earned:</strong> <code>Raise the Banners</code> 🎨</li>
</ul></li>
</ul>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Learn Sails.js - Part 2]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/learn-sails-js-part-2" />
      <id>tag:https:2025:/next-direction.de/2.34</id>
      <published>2025-08-31T10:00:00Z</published>
      <updated>2025-08-17T18:18:46Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-2.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-2.jpg" alt="part-2.jpg"/><br>
        <p>Let&#8217;s raise the anchor and learn to steer our new vessel.</p>

<hr />

<h3><strong>Part 2: Navigating the Harbor (Routes &amp; Controllers)</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Seaman</li>
<li><strong>Objective:</strong> To build and install the ship&#8217;s rudder and steering mechanism, allowing you to control how your application responds to specific requests from the outside world.</li>
</ul>

<hr />

<h4><strong>The Captain&#8217;s Briefing (Background and Theory)</strong></h4>

<p>Our sloop, <code>the-sea-serpent</code>, is afloat. It&#8217;s a magnificent sight, but currently, it just bobs aimlessly in the harbor. To go anywhere, we need to steer. This module is all about building that steering mechanism. In Sails.js, this is handled by <strong>Routes</strong> and <strong>Controllers</strong>.</p>

<p><strong>Routes: The Official Shipping Lanes</strong></p>

<p>Imagine the harbor your ship is in. A <strong>Route</strong> is an officially charted shipping lane. It&#8217;s a rule you define that says, &#8220;When a vessel hails us on <em>this specific path</em> (a URL), direct it to <em>that specific dock</em> (a piece of your code).&#8221;</p>

<p>These rules live in a single, critical file: <code>config/routes.js</code>. This file is your master map of the harbor.</p>

<p>A basic route definition looks like this:
<code>'GET /path/to/some/url': 'ControllerName.actionName'</code></p>

<ul>
<li><code>GET</code>: This is the <strong>HTTP Verb</strong> or method. <code>GET</code> is used for retrieving data, like when you type a URL into a browser address bar. We&#8217;ll encounter others like <code>POST</code> (for creating data) later.</li>
<li><code>/path/to/some/url</code>: This is the URL path that triggers the route.</li>
<li><code>'ControllerName.actionName'</code>: This is the destination, the specific &#8220;dock&#8221; where the request will be handled.</li>
</ul>

<p><strong>Controllers: The Harbor Masters</strong></p>

<p>If a route is the shipping lane, a <strong>Controller</strong> is the Harbor Master. It&#8217;s a file in your <code>api/controllers/</code> directory that contains the logic for handling requests. You can have many controllers, each one a specialist in handling a certain type of traffic.</p>

<p>Each function inside a controller is called an <strong>Action</strong>. An action is the specific code that runs when a route points to it. It&#8217;s the individual Harbor Master at the dock who personally greets the incoming ship.</p>

<p>Every action receives two crucial objects as arguments:</p>

<ul>
<li><code>req</code> (Request): An object containing all information about the incoming request. It&#8217;s the message in a bottle from the user, telling you what URL they visited, what data they sent, and who they are.</li>
<li><code>res</code> (Response): An object containing a set of methods for you to send a response back to the user. It&#8217;s your toolkit for sending a message back. You can send back raw data (<code>res.json()</code>), a full web page (<code>res.view()</code>), or just a simple confirmation (<code>res.ok()</code>).</li>
</ul>

<p>Today, we will generate our first Controller and create an action that sends a simple JSON response back to the user.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>Route</code>: A rule in <code>config/routes.js</code> that maps a URL to a controller action.</li>
<li><code>Controller</code>: A file (<code>api/controllers/YourController.js</code>) that groups related actions.</li>
<li><code>Action</code>: A function inside a controller that handles a single request.</li>
<li><code>req</code> (Request Object): Contains information about the incoming request.</li>
<li><code>res</code> (Response Object): Used to send a response back to the user.</li>
<li><code>res.json()</code>: A response method that sends data formatted as JSON.</li>
<li><code>sails generate controller</code>: The CLI command to create a new, empty controller file.</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;Establish a Port of Call&#8221;</strong></h4>

<p>In this quest, we will create a custom URL <code>/hail</code>. When a user visits it, our application will respond with a friendly JSON greeting.</p>

<ul>
<li><p><strong>Task 1: Recruit a Harbor Master (Generate a Controller)</strong></p>

<ol>
<li>Make sure your terminal is still inside your <code>the-sea-serpent</code> project directory.</li>
<li><p>Use the Sails &#8220;generate&#8221; command to create a controller for your Captain.</p>

<pre><code class="bash">sails generate controller CaptainController
</code></pre></li>
<li>Sails will confirm it has created the file <code>api/controllers/CaptainController.js</code>.</li>
</ol></li>
<li><p><strong>Task 2: Define Your Orders (Create the Action)</strong></p>

<ol>
<li>Open the new <code>api/controllers/CaptainController.js</code> file in your code editor. It will be mostly empty.</li>
<li><p>Add the <code>greet</code> action to it. Replace the contents of the file with the following code:</p>

<pre><code class="javascript">module.exports = &#123;

  greet: function(req, res) &#123;
    // This action sends a JSON response.
    // JSON is a standard format for sending data on the web.
    return res.json(&#123;
      message: 'Ahoy there, Captain!'
    &#125;);
  &#125;

&#125;;
</code></pre></li>
</ol></li>
<li><p><strong>Task 3: Chart the Shipping Lane (Create the Route)</strong></p>

<ol>
<li>Now, open the harbor map: <code>config/routes.js</code>.</li>
<li><p>Inside the <code>module.exports.routes</code> object, add a new line to define our route. The best place is just above the <code>'/': &#123; view: 'pages/homepage' &#125;</code> line.</p>

<pre><code class="javascript">module.exports.routes = &#123;

  /***************************************************************************
   *                                                                          *
   * Make the view located at `views/homepage.ejs` your home page.            *
   *                                                                          *
   * (Alternatively, remove this route and create an `index.html` file in     *
   * the `assets` directory)                                                  *
   *                                                                          *
   ***************************************************************************/

  'GET /hail': 'CaptainController.greet', // &lt;-- ADD THIS LINE

  '/': &#123; view: 'pages/homepage' &#125;,

  /***************************************************************************
   *                                                                          *
   * More custom routes here...                                               *
   * (See https://sailsjs.com/config/routes for examples.)                    *
   *                                                                          *
   * If a request to a URL doesn't match any of the routes in this file, it   *
   * is matched against "shadow routes" (e.g. blueprint routes).  If it does  *
   * not match any of those, it is matched against static assets.             *
   *                                                                          *
   ***************************************************************************/

&#125;;
</code></pre></li>
</ol></li>
<li><p><strong>Task 4: Test Your New Route</strong></p>

<ol>
<li>If your server is already running, stop it (<code>CTRL</code> + <code>C</code>) and restart it with <code>sails lift</code> to make sure it recognizes your new controller and route changes.</li>
<li>Open your web browser and navigate to <code>http://localhost:1337/hail</code>.</li>
<li>Instead of a web page, you should see the raw JSON text you defined in your action:
<code>&#123;"message":"Ahoy there, Captain!"&#125;</code></li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Mission accomplished! Our ship now has a working rudder. You have successfully created a custom endpoint for your application.</p>

<p>Let&#8217;s trace the journey of that request:
1.  Your browser sent a <code>GET</code> request to <code>/hail</code>.
2.  The Sails router checked the <code>config/routes.js</code> file and found a matching rule.
3.  That rule directed the request to the <code>greet</code> action inside the <code>CaptainController</code>.
4.  Your action&#8217;s code executed, using <code>res.json()</code> to send a greeting back to the browser.</p>

<p>You are now in control. You can define any URL and make your application do anything you want in response. Right now, we&#8217;re just sending data, but soon we&#8217;ll use this same mechanism to display full pages, save data, and build complex features.</p>

<hr />

<h4><strong>Rewards &amp; Promotion</strong></h4>

<ul>
<li><strong>+150 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You&#8217;ve created your first working route and controller action. You are no longer just floating; you are navigating!

<ul>
<li><strong>Badge Earned:</strong> <code>First Heading</code> 🧭</li>
</ul></li>
<li><strong>Promotion to: Boatswain</strong></li>
</ul>

<p>Excellent work, Boatswain. You have authority on deck. Now, it&#8217;s time to make our ship presentable to visitors.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Learn Sails.js - Part 1]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/learn-sails-js-part-1" />
      <id>tag:https:2025:/next-direction.de/2.33</id>
      <published>2025-08-24T10:00:00Z</published>
      <updated>2025-08-17T18:12:09Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-1.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-1.jpg" alt="part-1.jpg"/><br>
        <p>Let&#8217;s lay the keel for our first vessel.</p>

<hr />

<h3><strong>Part 1: Commissioning Your First Sloop</strong></h3>

<ul>
<li><strong>Current Rank:</strong> Seaman</li>
<li><strong>Objective:</strong> To use the Sails.js blueprints to construct and launch your very first application, a simple, elegant sloop ready for its maiden voyage.</li>
</ul>

<hr />

<h4><strong>The Captain&#8217;s Briefing (Background and Theory)</strong></h4>

<p>Welcome to the main shipyard, Seaman. Your theoretical training is over; it&#8217;s time to get your hands dirty. In this module, we will take the <code>Sails.js</code> blueprint you acquired and command the shipyard to construct our first ship. This process is called <strong>scaffolding</strong>.</p>

<p><strong>The <code>sails new</code> Command: Laying the Keel</strong></p>

<p>The <code>sails new &lt;your-app-name&gt;</code> command is your first order as a shipbuilder. When you issue this command, Sails acts as your master foreman. It doesn&#8217;t just create an empty folder; it <strong>scaffolds</strong> a complete project structure. It lays the keel, erects the frame, and builds out all the necessary decks and compartments according to the proven MVC blueprint. This saves you from the tedious and error-prone task of creating every file and folder by hand.</p>

<p><strong>Anatomy of a Sloop: The Generated Folder Structure</strong></p>

<p>Once the scaffolding is complete, you&#8217;ll have a new directory filled with files and other folders. At first, it might look intimidating, but it&#8217;s logically organized like a real ship. Let&#8217;s inspect the three most important areas for now:</p>

<ul>
<li><p><code>api/</code> <strong>(The Command Deck &amp; Crew Quarters):</strong> This is the heart of your application&#8217;s logic. All your &#8220;crew&#8221; live here.</p>

<ul>
<li><code>controllers/</code>: The bridge, where your Captains (Controllers) will live.</li>
<li><code>models/</code>: The cargo hold, where the manifests for your data (Models) will be stored.</li>
<li><code>helpers/</code>, <code>services/</code>, <code>policies/</code>: Quarters for specialist crew members we will hire in later voyages.</li>
</ul></li>
<li><p><code>config/</code> <strong>(The Ship&#8217;s Log &amp; Rulebook):</strong> This folder is the administrative center of your ship. It contains all the configuration files. You don&#8217;t need to memorize them all, but know that this is where you define your ship&#8217;s rules, from routes (<code>routes.js</code>) to database connections (<code>datastores.js</code>) and environment-specific settings.</p></li>
<li><p><code>views/</code> <strong>(The Public-Facing Decks):</strong> This is where you design what your ship looks like to the outside world. It contains the EJS (<code>.ejs</code>) template files that will be rendered into the HTML pages your users see. You&#8217;ll find the default <code>homepage.ejs</code> here.</p></li>
<li><p><code>assets/</code> <strong>(The Paint, Rigging, and Flags):</strong> This directory holds all of your static front-end assets. This includes your stylesheets (CSS), client-side JavaScript files, images, fonts, and anything else the user&#8217;s browser needs to download. Sails has a powerful asset pipeline that will automatically bundle and prepare these for you.</p></li>
</ul>

<p><strong>The <code>sails lift</code> Command: Launching into the Water</strong></p>

<p>A ship in dry-dock is just a sculpture. The <code>sails lift</code> command is the order to &#8220;open the floodgates and launch the vessel!&#8221; When you run this command from inside your project directory, Sails.js does several things:</p>

<ol>
<li>It reads all your configuration files.</li>
<li>It loads all your models, controllers, and services.</li>
<li>It starts a web server on your computer.</li>
<li>It begins listening for incoming HTTP requests on a specific port (the default is <strong>1337</strong>).</li>
</ol>

<p>The famous Sails ASCII art of a ship that appears in your terminal is your confirmation: your ship is now afloat in your local development harbor (<code>localhost</code>).</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>sails new</code>: The command to scaffold a new Sails.js application.</li>
<li><code>sails lift</code>: The command to start the Sails server.</li>
<li><code>Scaffolding</code>: The process of auto-generating a project&#8217;s folder and file structure.</li>
<li><code>Project Structure</code>: The purpose of the <code>api/</code>, <code>config/</code>, <code>views/</code>, and <code>assets/</code> directories.</li>
<li><code>localhost</code>: A hostname that refers to the current computer, your &#8220;local harbor.&#8221;</li>
<li><code>Port</code>: The numbered &#8220;dock&#8221; your application is listening on (default: <code>1337</code>).</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;Launch Ceremony&#8221;</strong></h4>

<p>It&#8217;s time to build. Follow these orders precisely to construct and launch <code>the-sea-serpent</code>.</p>

<ul>
<li><p><strong>Task 1: Order the Construction</strong></p>

<ol>
<li>Navigate your terminal to a directory where you keep your projects (e.g., <code>Documents/projects</code>).</li>
<li><p>Run the <code>sails new</code> command to scaffold your ship.</p>

<pre><code class="bash">sails new the-sea-serpent
</code></pre></li>
<li>You will see Sails creating a list of files and folders. Once it&#8217;s done, it will prompt you. Choose <strong>1. Web App</strong> for now.</li>
</ol></li>
<li><p><strong>Task 2: Board Your Vessel</strong></p>

<ol>
<li><p>Your ship has been built in a new directory. You must now go inside it. Use the <code>cd</code> (change directory) command:</p>

<pre><code class="bash">cd the-sea-serpent
</code></pre></li>
<li>Your terminal prompt should now show that you are inside the <code>the-sea-serpent</code> directory. All future <code>sails</code> commands for this project must be run from here.</li>
</ol></li>
<li><p><strong>Task 3: Inspect the Ship</strong></p>

<ol>
<li>Open the <code>the-sea-serpent</code> folder in your favorite code editor (like VS Code, Sublime Text, or Atom).</li>
<li>Spend two minutes just looking at the folder structure. Find the <code>api/controllers</code>, <code>config/routes.js</code>, and <code>views/homepage.ejs</code> files. Don&#8217;t change anything; just get familiar with the layout.</li>
</ol></li>
<li><p><strong>Task 4: Launch!</strong></p>

<ol>
<li><p>Back in your terminal (make sure you&#8217;re still in the <code>the-sea-serpent</code> directory), give the order to launch.</p>

<pre><code class="bash">sails lift
</code></pre></li>
<li><p>Wait a moment. You should see the Sails logo, some version information, and finally, the success message:</p>

<pre><code class="bash">info: Server lifted in `/path/to/your/project/the-sea-serpent`
info: To shut down Sails, press &lt;CTRL&gt; + C at any time.

info:                .-..-.
info:
info:    Sails              &lt;|    .-..-.
info:    v1.5.15             |\
info:                       /|.\
info:                      / || \
info:                    ,'  |'  \
info:                 .-'.-==|/_--'
info:                 `--'-------'
info:    __---___--___---___--___---___--___
info:  ____---___--___---___--___---___--___-__

info: Running in development mode.
info: Link to your app: http://localhost:1337
</code></pre></li>
</ol></li>
<li><p><strong>Task 5: The Maiden Voyage View</strong></p>

<ol>
<li>Open your favorite web browser (Chrome, Firefox, etc.).</li>
<li>In the address bar, type <code>http://localhost:1337</code> and press Enter.</li>
<li>You should see the default Sails.js welcome page. This page is being served by the Sails server you just started!</li>
</ol>

<p>To stop the server, go back to your terminal window and press <code>CTRL</code> + <code>C</code>.</p></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Congratulations, Seaman! You have successfully commanded the construction and launch of your first vessel, <code>the-sea-serpent</code>. It is no longer just a collection of files; it is a living, breathing web server floating in your local harbor, ready to receive orders.</p>

<p>You have mastered the two most fundamental commands in the framework: <code>sails new</code> and <code>sails lift</code>. You have also taken your first tour of a Sails.js application&#8217;s structure, a layout that will soon become second nature to you.</p>

<p>The ship is afloat, but it currently has no rudder. It can only sit at the dock. In our next part, we will step onto the bridge and learn how to steer.</p>

<hr />

<h4><strong>Rewards</strong></h4>

<ul>
<li><strong>+100 Doubloons</strong></li>
<li><strong>Achievement Unlocked:</strong> You&#8217;ve started your server for the first time. The shipyard crew raises a banner in your honor!

<ul>
<li><strong>Badge Earned:</strong> <code>Hoist the Colors</code> 🚩</li>
</ul></li>
</ul>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Learn Sails.js - Part 0]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/learning-sails.js-part-0" />
      <id>tag:https:2025:/next-direction.de/2.32</id>
      <published>2025-08-17T10:00:00Z</published>
      <updated>2025-08-17T18:06:46Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/part-0.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/part-0.jpg" alt="part-0.jpg"/><br>
        <p>Welcome back! I hope you will enjoy the next few posts introducing the Sails.js web framework. To make it more fun I thought I make it into a little RPG like game. So let&#8217;s get started with part 0 which is more of a pre-stage to make your system ready for Sails.js.</p>

<hr />

<h3><strong>Pre-Voyage Checklist: The Shipyard</strong></h3>

<ul>
<li><strong>Rank:</strong> Civilian</li>
<li><strong>Objective:</strong> To ensure you are properly equipped, understand the mission, and have the foundational knowledge to begin your voyage with Sails.js.</li>
</ul>

<hr />

<h4><strong>The Captain&#8217;s Briefing (Background and Theory)</strong></h4>

<p>Welcome to the shipyard, future Captain. Before you can command a fleet, you must first understand the vessel. A modern web application is a complex machine, and building one from scratch using only raw materials is a monumental task. You would need to forge every screw, mill every plank, and weave every rope yourself. That is the world of coding without a framework.</p>

<p>This is where your <strong>master blueprint</strong>, Sails.js, comes in. Sails is a <strong>web framework</strong>. Think of it as a master-crafted ship kit. The hull&#8217;s design is already laid out, the key structural components are pre-fabricated, and there&#8217;s a logical set of instructions for assembling everything. This lets you focus on the important parts, the cargo you&#8217;ll carry, the routes you&#8217;ll travel, and the unique features of your ship, rather than on the basics of naval architecture.</p>

<p><strong>The Engine Room: Node.js and npm</strong>
Your ship will sail on the vast &#8220;Digital Sea,&#8221; which is powered by <strong>Node.js</strong>. Node.js is a runtime environment that allows you to run JavaScript code on a server, outside of a web browser. It&#8217;s the engine that will power your entire application.</p>

<p>Every engine needs parts, fuel, and tools. For Node.js, that supply store is <strong>npm (Node Package Manager)</strong>. It&#8217;s the world&#8217;s largest software registry, the grand bazaar where you&#8217;ll acquire all the &#8220;packages&#8221; (pre-written code) you need, including the Sails.js framework itself.</p>

<p><strong>The Master Blueprint: The MVC Architecture</strong>
Sails.js, like many professional frameworks, is built upon a time-tested architectural pattern called <strong>MVC (Model-View-Controller)</strong>. Understanding this is the single most important concept for your journey. It&#8217;s how your ship is organized.</p>

<ul>
<li><p><strong>M - Model (The Cargo &amp; The Manifest):</strong> The Model is responsible for all your application&#8217;s data. It is the ship&#8217;s cargo hold and, more importantly, the <em>cargo manifest</em>. It defines the rules for your data: what kind of cargo (or &#8220;data&#8221;) can you carry? What are its properties (e.g., a <code>user</code> has a <code>name</code> and <code>email</code>)? How is it stored? The Model is your single source of truth for everything data-related.</p></li>
<li><p><strong>V - View (The Ship&#8217;s Appearance):</strong> The View is what people see. It&#8217;s the polished deck, the painted hull, the sails snapping in the wind, and the flag flying from the mast. In web terms, this is the user interface (UI), the HTML, CSS, and front-end JavaScript that gets rendered in the user&#8217;s browser.</p></li>
<li><p><strong>C - Controller (The Captain on the Bridge):</strong> The Controller is the brain of the operation. It&#8217;s the captain standing at the ship&#8217;s wheel. When an order comes in (an HTTP request from a user, like clicking a link), the Controller is the one who interprets it. It might command the Model to fetch or update data from the cargo hold and then command the View to display the results. It connects the user&#8217;s actions to the application&#8217;s data.</p></li>
</ul>

<p>By separating these three concerns, Sails keeps your code organized, scalable, and much easier to manage.</p>

<hr />

<h4><strong>Key Concepts Checklist</strong></h4>

<ul>
<li><code>Node.js</code>: The JavaScript server environment.</li>
<li><code>npm</code>: The package manager for Node.js.</li>
<li>Web Framework: A structured toolkit for building web applications.</li>
<li><code>MVC Architecture</code>: The Model-View-Controller design pattern.</li>
<li><code>Sails.js CLI</code>: The command-line interface you&#8217;ll use to create and manage Sails projects.</li>
<li><code>Convention over Configuration</code>: The principle that the framework makes smart default choices for you, speeding up development.</li>
</ul>

<hr />

<h4><strong>Mission Log: Quest - &#8220;Sign the Ship&#8217;s Articles&#8221;</strong></h4>

<p>This quest ensures your development environment is correctly set up and you&#8217;ve understood the core principles.</p>

<ul>
<li><p><strong>Task 1: Procure Your Engine (Install Node.js &amp; npm)</strong></p>

<ol>
<li>Navigate to the <a href="https://nodejs.org/">official Node.js website</a>.</li>
<li>Download and install the LTS (Long Term Support) version for your operating system. <code>npm</code> is included automatically with the Node.js installation.</li>
<li><p><strong>Verify the installation:</strong> Open your computer&#8217;s terminal or command prompt and run the following two commands, one after the other. You should see version numbers output for each.</p>

<pre><code class="bash">node -v
npm -v
</code></pre></li>
</ol></li>
<li><p><strong>Task 2: Acquire the Master Blueprints (Install Sails.js)</strong></p>

<ol>
<li><p>In your terminal, run the following command to install the Sails.js package <strong>globally</strong> on your system. The <code>-g</code> flag makes the <code>sails</code> command available everywhere.</p>

<pre><code class="bash">npm install -g sails
</code></pre></li>
<li><p><strong>Verify the installation:</strong> Run the <code>sails</code> version command.</p>

<pre><code class="bash">sails -v
</code></pre>

<p>You should see the version number of your Sails installation.</p></li>
</ol></li>
<li><p><strong>Task 3: Take the MVC Oath (Prove Your Knowledge)</strong></p>

<ol>
<li>Open a simple text file or grab a piece of paper.</li>
<li>Without looking back at the briefing, write one sentence for each of the MVC components, describing its role using the ship analogy.</li>
<li>This is for your benefit only, a mental check to ensure the core concept is clear before we start building.</li>
</ol></li>
</ul>

<hr />

<h4><strong>Mission Debrief (Review &amp; Outcomes)</strong></h4>

<p>Excellent work. You have successfully provisioned your shipyard. Your local machine is now a fully-featured development environment, equipped with the runtime, package manager, and framework blueprints necessary for the journey ahead.</p>

<p>Most importantly, you now understand the fundamental MVC pattern that governs our work. This separation of concerns is the key to building clean, powerful applications.</p>

<p>Your tools are ready. Your mind is prepared. The next step is to use those blueprints to construct your very first vessel.</p>

<hr />

<h4><strong>Rewards &amp; Promotion</strong></h4>

<ul>
<li><strong>+50 Doubloons</strong></li>
<li><strong>Promotion to: Seaman</strong></li>
</ul>

<p>Welcome aboard, Seaman. Your voyage begins <strong>now</strong>.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[TSMCs Rolle für die Halbleiterindustrie]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/tsmcs-rolle-fuer-die-halbleiterindustrie" />
      <id>tag:https:2025:/next-direction.de/2.30</id>
      <published>2025-03-29T08:00:00Z</published>
      <updated>2025-03-29T07:02:25Z</updated>
      <author>
            <name>ndn8n-r</name>
            <email>info@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/chip-factory.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/chip-factory.jpg" alt="chip-factory.jpg"/><br>
        <h1>Die Rolle von TSMC in der globalen Halbleiterindustrie</h1>

<p>In der heutigen Zeit ist die Abhängigkeit von TSMC, dem führenden Auftragsfertiger von Halbleitern, von zentraler Bedeutung für die globale Technologiebranche. Dieser Artikel beleuchtet die zahlreichen Dimensionen dieser Abhängigkeit, die über wirtschaftliche Verbindungen hinausgehen und auch geopolitische Implikationen mit sich bringen. Es wird untersucht, wie TSMCs dominierende Marktstellung und technologische Führungsposition sowohl Chancen als auch Risiken für Unternehmen und Länder darstellen und welche Strategien zur Diversifizierung notwendig sind. Das Verständnis dieser Themen ist entscheidend, um die Zukunft der Halbleiterindustrie zu gestalten.</p>

<h2>Einfluss von TSMC auf die Halbleiterindustrie</h2>

<p>TSMC, die Taiwan Semiconductor Manufacturing Company, spielt eine unvergleichliche Rolle in der globalen Halbleiterindustrie, die weit über technische Innovationen hinausgeht und sowohl wirtschaftliche als auch politische Dimensionen beeinflusst. Mit über 50 % Marktanteil im globalen Foundry-Segment hat TSMC einen entscheidenden Einfluss auf die technologische Entwicklung auf internationaler Ebene. Das Unternehmen ist nicht nur das wertvollste Technologieunternehmen in Taiwan, sondern auch das neuntwertvollste Unternehmen weltweit mit einer Marktbewertung von 892 Milliarden US-Dollar, was die wirtschaftliche Bedeutung von TSMC unterstreicht [1].</p>

<p>Die Innovationskraft von TSMC, insbesondere bei der Massenproduktion von 2-Nanometer-Chips, versetzt das Unternehmen in eine bevorzugte Position gegenüber seinen Mitbewerbern wie Samsung und Intel, die etwa ein bis drei Generationen hinter TSMC liegen [2]. Diese technologische Überlegenheit führt dazu, dass bedeutende Unternehmen wie Apple und Nvidia auf die fortschrittlichen Fertigungskapazitäten von TSMC angewiesen sind, was deutlich macht, wie TSMCs Dominanz die gesamte Technologielandschaft prägt [3].</p>

<p>Politisch hat TSMCs zentrale Rolle im globalen Halbleitermarkt erhebliche Auswirkungen auf die geopolitischen Dynamiken, insbesondere in Bezug auf die Spannungen zwischen den USA und China. Die Notwendigkeit, die taiwanesische Halbleiterindustrie zu schützen und gleichzeitig die nationalen Sicherheitsbedenken bei der Abhängigkeit von ausländischer Technologie anzusprechen, führt zu politischen Debatten und Strategien, die Taiwan als Schlüsselressource in den internationalen Beziehungen positionieren. Statt abzuweichen, ist TSMC zunehmend gezwungen, in neue Fabriken in den USA zu investieren, um sich an die sich verändernde geopolitische Landschaft anzupassen [4].</p>

<p>Die Abhängigkeit von TSMC hat somit weitreichende wirtschaftliche und politische Implikationen, die sowohl Chancen als auch Herausforderungen mit sich bringen. Während TSMC weiterhin die technologische Speerspitze der Branche bildet, stehen die globalen Märkte vor der Aufgabe, mit den Risiken und der Unsicherheit, die sich aus einer so zentralisierten Produktionsstruktur ergeben, umzugehen [5].</p>

<hr />

<h2>TSMCs Marktbeherrschung und Umsatz</h2>

<p>TSMC bleibt im Jahr 2024 der unangefochtene Spitzenreiter unter den Auftragsfertigern in der globalen Halbleiterindustrie. Das Unternehmen erzielte einen beeindruckenden Umsatz von etwa 90 Milliarden US-Dollar, bei einem Gewinn von rund 36 Milliarden US-Dollar, was einer Umsatzrendite von 40 % entspricht [6]. Im Vergleich zu anderen großen Herstellern wie Samsung und Intel weist TSMC bemerkenswerte Wachstumsraten auf. Im vierten Quartal 2024 verzeichnete das Unternehmen einen Umsatz von 26,88 Milliarden US-Dollar, was einem Wachstum von 37 % im Vergleich zum Vorjahr entspricht [7].</p>

<p>Die Marktführerschaft von TSMC wird maßgeblich durch mehrere entscheidende Faktoren unterstützt. Zuallererst profitiert das Unternehmen von der steigenden Nachfrage nach innovativen Halbleiterlösungen, insbesondere in schnell wachsenden Bereichen wie künstlicher Intelligenz und High-Performance Computing. Der Umsatz mit der innovativen 3nm-Prozesstechnologie macht inzwischen über ein Viertel des Gesamtergebnisses aus, was TSMC einen Wettbewerbsvorteil gegenüber anderen Herstellern verschafft, die möglicherweise weniger fortschrittliche Prozesse anbieten [8].</p>

<p>Des Weiteren hat TSMC bedeutende Investitionen in seine Produktionskapazitäten getätigt, unter anderem in neuen Werken in den USA, Japan und Deutschland. Diese Expandierungsstrategien zielen darauf ab, die weltweite Nachfrage zu bedienen und potenzielle geopolitische Risiken, die mit der Fertigung in Taiwan verbunden sind, abzumildern [9]. Trotz der Herausforderungen, die durch geopolitische Spannungen und natürliche Katastrophen wie das jüngste Erdbeben in Taiwan entstanden sind, bleibt TSMC ein dominierender Akteur in der Halbleiterindustrie und hat die Planung für zukünftige Ertragssteigerungen nicht aus den Augen verloren.</p>

<hr />

<h2>Technologische Innovation bei TSMC</h2>

<p>Die technologische Innovation von TSMC ist ein zentraler Faktor, der das Unternehmen von seinen Mitbewerbern in der Halbleiterindustrie abhebt. Ein herausragendes Beispiel ist die bevorstehende Massenerzeugung von 2nm-Chips, die TSMC im Jahr 2025 beginnen wird. Dieser Fortschritt stellt einen bedeutenden Sprung in der Halbleitertechnologie dar, der nicht nur die Leistung, sondern auch die Energieeffizienz erheblich verbessert. Im Vergleich zum aktuellen 3nm-Knoten wird ein Leistungszuwachs von etwa 15 % und eine Reduktion des Stromverbrauchs um bis zu 30 % erwartet [10].</p>

<p>Ein weiteres bemerkenswertes Merkmal der 2nm-Technologie ist die Einführung der Gate-All-Around (GAA) Technologien. Diese Nanosheet-Transistoren ermöglichen eine verbesserte Kontrolle über den Stromfluss und bieten eine höhere Transistordichte, was die Effizienz weiter steigert [11]. Zudem wird TSMC sein N2 NanoFlex Design integrieren, um die vorgenannten Fortschritte zu unterstützen und den Anforderungen zukünftiger Anwendungen gerecht zu werden [12].</p>

<p>Darüber hinaus investiert TSMC in fortschrittliche Verpackungstechnologien und kombiniert die 2nm-Technologie mit 3DIC-Fähigkeiten, die in Anwendungen wie KI, Hochleistungsrechnern (HPC) und mobilen System-on-Chip (SoCs) erheblich genutzt werden [13]. Diese Innovationspipeline positioniert TSMC als Marktführer im Bereich der Halbleitertechnologie und ermöglicht es dem Unternehmen, Trends in Schlüsselindustrien wie KI und autonomen Fahrzeugen anzuführen.</p>

<p>Neben diesen technischen Fortschritten ist TSMC auch aktiv in der Erforschung von Quantencomputing, was weiteres Potenzial für zukünftige Anwendungen in der Datenanalyse eröffnet [14]. Solche kontinuierlichen Investitionen in Forschung und Entwicklung sind entscheidend, um TSMCs Position an der Spitze der Branche zu halten und sich in einem zunehmend wettbewerbsintensiven Markt zu behaupten.</p>

<hr />

<h2>Risiken der Abhängigkeit von TSMC</h2>

<p>Die zunehmende Abhängigkeit von TSMC (Taiwan Semiconductor Manufacturing Company) hat vielfältige Herausforderungen und Risiken hervorgebracht, insbesondere für fabless Unternehmen, die auf die hochentwickelten Fertigungskapazitäten von TSMC angewiesen sind. Die Dominanz von TSMC im globalen Halbleitermarkt ist unbestritten, mit einem Marktanteil von über 50 % im Bereich der Chip-Foundry-Dienstleistungen. Diese Konzentration der Produktion führt zu einer erhöhten Anfälligkeit der globalen Lieferketten gegenüber Störungen [8].</p>

<p>Die geopolitischen Spannungen, vor allem zwischen Taiwan und China, stellen ein weiteres signifikantes Risiko dar. Solche Konflikte könnten nicht nur direkte Auswirkungen auf die Produktionskapazitäten von TSMC haben, sondern auch das Vertrauen der fabless Unternehmen untergraben, die auf kontinuierliche und zuverlässige Lieferungen angewiesen sind [15].</p>

<p>Lieferkettenunterbrechungen durch Naturkatastrophen oder politische Konflikte könnten zu einem erheblichen Rückgang in der globalen Chipproduktion führen, was sich verheerend auf zahlreiche Wirtschaftszweige, von Automobilherstellern bis hin zu Elektronikunternehmen, auswirken kann. Störungen in der Chipproduktion führen nicht nur zu Maßnahmen zur Aufrechterhaltung der Produktion, sondern bringen auch wirtschaftliche Verluste mit sich, die die Innovationsfähigkeit von Unternehmen gefährden [16].</p>

<p>Darüber hinaus sind fabless Unternehmen wie Nvidia stark von den fortschrittlichen Fertigungsprozessen TSMCs abhängig. Diese Abhängigkeit kann deren Fähigkeit zur technologischen Innovation beeinträchtigen und sie in ihrer Wettbewerbsfähigkeit einschränken [17]. Auch die Übergänge von Produktionskapazitäten zu spezialisierten Foundries wie TSMC können zu Arbeitsplatzverlusten in traditionellen Fertigungssektoren führen, was soziale Auswirkungen nach sich zieht [18].</p>

<p>In Anbetracht dieser Herausforderungen bemühen sich verschiedene Länder und Unternehmen, ihre Lieferketten zu diversifizieren und lokalisiertes Produktionskapazitäten auszubauen, um das Risiko einer übermäßigen Abhängigkeit von TSMC zu mindern und die Stabilität der globalen Halbleiterindustrie zu fördern [19].</p>

<hr />

<h2>Strategien zur Diversifizierung der Halbleiterproduktion</h2>

<p>Um die Abhängigkeiten in der Halbleiterproduktion zu reduzieren, verfolgen Unternehmen und Regierungen verschiedene Diversifizierungsstrategien, die sowohl technologische als auch geopolitische Aspekte berücksichtigen. Eine der zentralen Ansätze ist die geographische Diversifizierung der Produktionsstätten. Dabei wird angestrebt, Halbleiterfertigungsfähigkeiten in unterschiedlichen Ländern zu etablieren, um die Abhängigkeit von bestimmten Regionen, wie beispielsweise Taiwan oder Südkorea, zu minimieren [20].</p>

<p>Zusätzlich wird die Resilienz der Lieferketten verstärkt, indem Unternehmen ihre Zulieferer und Produktionsstandorte diversifizieren. Diese Strategie ist entscheidend, um Engpässe zu vermeiden und die operationale Stabilität bei möglichen Störungen zu gewährleisten [21].</p>

<p>Ein weiterer wichtiger Faktor ist die Investition in nationale Produktionskapazitäten. So haben Länder wie die USA mit dem CHIPS Act und die EU mit eigenen Initiativen Emissionen nach dem Ziel, die lokale Fertigung zu stärken und damit die Abhängigkeit von ausländischen Quellen zu verringern [22].</p>

<p>Technologisch wird auch der Fokus auf neue Verpackungsverfahren wie Chiplets und 3D-Integration gelegt. Diese Innovationsansätze ermöglichen eine flexiblere und modulare Chip-Entwicklung, die über die traditionellen Skalierungsgrenzen hinausgeht [23].</p>

<p>Darüber hinaus ist die Forschung an alternativen Halbleitermaterialien wie Gallium-Nitrid (GaN) und Siliziumkarbid (SiC) von wachsender Bedeutung, um die Leistungsfähigkeit zu verbessern und neue Anwendungsbereiche zu erschließen [24].</p>

<p>Die Schaffung eines stark entwickelten Talentpools zur Rekrutierung und Ausbildung von Fachkräften sowie internationale Kooperationen zwischen verbündeten Nationen, die den Technologietransfer und Ressourcenteilung fördern, sind ebenfalls wesentliche Strategien, die darauf abzielen, eine widerstandsfähigere Halbleiterindustrie zu schaffen, die geopolitische Drucksituationen erfolgreich ausgleichen kann [25].</p>

<hr />

<h2>Schlussfolgerungen</h2>

<p>Die Abhängigkeit von TSMC verdeutlicht die intricaten Verflechtungen zwischen Technologie, Wirtschaft und Geopolitik in der modernen Welt. Dieses Thema ist nicht nur von akademischem Interesse, sondern hat direkte Auswirkungen auf die Strategie von Unternehmen und die politische Planung zur Sicherstellung stabiler Lieferketten. Eine ausgewogene Betrachtung der technologischen Innovation und der Notwendigkeit zur Diversifizierung der Halbleiterproduktion ist unabdingbar für die Zukunftsfähigkeit der Branche. Die Ergebnisse dieser Analyse bieten wertvolle Einblicke, um fundierte Entscheidungen in einer zunehmend komplexen globalen Landschaft zu treffen.</p>

<h2>Quellen</h2>

<ol>
<li><a href="https://techsoda.substack.com/p/a-deep-dive-in-how-tsmc-won-its-place">Tech Soda - A Deep Dive in How TSMC Won Its Place</a></li>
<li><a href="https://wallstreetpit.com/122713-tsmcs-tech-dominance-3-generations-ahead-of-domestic-rivals-1-ahead-globally/">Wall Street Pit - TSMC&#8217;s Tech Dominance: 3 Generations Ahead of Domestic Rivals, 1 Ahead Globally</a></li>
<li><a href="https://ainvest.com/news/taiwan-semiconductor-dominance-call-global-collaboration-2502/">AInvest - Taiwan Semiconductor Dominance Calls for Global Collaboration</a></li>
<li><a href="https://dset.tw/en/media-report-en/dset-op-ed-on-the-diplomat-analyzes-trumps-semiconductor-policy-and-its-implications-for-taiwan/">DSET - DSET Op-Ed on The Diplomat Analyzes Trump&#8217;s Semiconductor Policy and its Implications for Taiwan</a></li>
<li><a href="https://www.internationalaffairs.org.au/australianoutlook/has-the-shield-become-a-snare-taiwans-semiconductor-supremacy-and-the-challenge-of-economic-sovereignty/">International Affairs - Has the Shield Become a Snare? Taiwan&#8217;s Semiconductor Supremacy and the Challenge of Economic Sovereignty</a></li>
<li><a href="https://finment.com/boerse-aktien/halbleiter-aktien/tsmc-aktie-prognose/">Finment - TSMC Aktie Prognose</a></li>
<li><a href="https://de.marketscreener.com/kurs/aktie/TSMC-TAIWAN-SEMICONDUCTOR-6492349/news/TSMC-erzielt-2024-einen-zurechenbaren-Gewinn-von-1-173-Billionen-NT-49032023/">MarketScreener - TSMC erzielt 2024 einen zurechenbaren Gewinn von 1.173 Billionen NT$</a></li>
<li><a href="https://www.all-electronics.de/elektronik-entwicklung/top10-der-groessten-halbleiterhersteller-weltweit-338.html">All Electronics - Top 10 der größten Halbleiterhersteller weltweit</a></li>
<li><a href="https://www.focus.de/finanzen/boerse/umsatz-einbussen-erdbeben-erschuettert-bilanz-des-chip-riesen-tsmc_f0cc01bc-24fd-42ee-b6ba-c859620a21e2.html">Focus - Umsatz-Einbußen: Erdbeben erschüttert Bilanz des Chip-Riesen TSMC</a></li>
<li><a href="https://pcoutlet.com/parts/cpus/tsmcs-2025-deadline-will-arizonas-first-advanced-chip-fab-deliver">PC Outlet - TSMC&#8217;s 2025 Deadline: Will Arizona&#8217;s First Advanced Chip Fab Deliver</a></li>
<li><a href="https://www.astutegroup.com/news/industrial/tsmcs-2nm-venture-a-testament-to-innovation-amid-complexity/">Astute Group - TSMC&#8217;s 2nm Venture: A Testament to Innovation Amid Complexity</a></li>
<li><a href="https://semiwiki.com/semiconductor-services/techinsights/352972-iedm-2025-tsmc-2nm-process-disclosure-how-does-it-measure-up/">SemiWiki - IEDM 2025 TSMC 2nm Process Disclosure: How Does it Measure Up?</a></li>
<li><a href="https://dsa.si/uncategorised/how-tsmc-is-revolutionizing-the-semiconductor-industry-and-captivating-investors/22006/">DSA - How TSMC is Revolutionizing the Semiconductor Industry and Captivating Investors</a></li>
<li><a href="https://dsa.si/uncategorised/the-future-of-tsmc-engineering-the-next-tech-revolution-what-you-need-to-know/16795/">DSA - The Future of TSMC: Engineering the Next Tech Revolution</a></li>
<li><a href="https://option-news.at/it-tech/wird-tsmc-der-koenig-der-chips-lassen-sie-sich-das-nicht-entgehen/65194/">Option News - Wird TSMC der König der Chips?</a></li>
<li><a href="https://hplmachining.com/de/blog/is-taiwan-manufacturing-better-than-china/">HPL Machining - Ist die taiwanesische Herstellung besser als die in China?</a></li>
<li><a href="https://www.digitec.ch/de/page/weniger-ki-chips-fuer-die-schweiz-usa-schraenkt-exporte-fuer-120-staaten-ein-36507">Digitec - Weniger KI-Chips für die Schweiz: USA schränkt Exporte für 120 Staaten ein</a></li>
<li><a href="https://deutsche-wirtschafts-nachrichten.de/713663/nvidia-aktie-prognose-2025-mit-mehr-potential-als-risiko-nvidia-aktie-kursziel-ueberzeugt">Deutsche Wirtschafts Nachrichten - Nvidia Aktie Prognose 2025: Mit mehr Potential als Risiko</a></li>
<li><a href="https://www.it-boltwise.de/tsmc-plant-strategische-expansion-durch-moegliche-intel-uebernahme.html">IT Boltwise - TSMC plant strategische Expansion durch mögliche Intel-Übernahme</a></li>
<li><a href="https://www.ainvest.com/news/navigating-geopolitical-risks-in-semiconductor-industry-strategies-for-long-term-growth-25011010f3e7de5932041474">AInvest - Navigating Geopolitical Risks in Semiconductor Industry: Strategies for Long-Term Growth</a></li>
<li><a href="https://manufacturing.asia/news/how-semiconductor-firms-can-stay-resilient-amidst-geopolitical-risks">Manufacturing Asia - How Semiconductor Firms Can Stay Resilient Amidst Geopolitical Risks</a></li>
<li><a href="https://www.electronicdesign.com/technologies/eda/article/55265778/electronic-design-megatrends-set-to-transform-the-semiconductor-industry-in-2025">Electronic Design - Megatrends Set to Transform the Semiconductor Industry in 2025</a></li>
<li><a href="https://www.kessystemsinc.com/resources/latest-developments-in-semiconductor-technology/">Kessystems - Latest Developments in Semiconductor Technology</a></li>
<li><a href="https://www.kessystemsinc.com/resources/the-future-of-semiconductor-technology-innovations-trends-and-industry-outlook/">Kessystems - The Future of Semiconductor Technology: Innovations, Trends, and Industry Outlook</a></li>
<li><a href="https://www.lowyinstitute.org/the-interpreter/us-restriction-chipping-away-asean-s-semiconductor-future">Lowy Institute - U.S. Restriction Chipping Away at ASEAN&#8217;s Semiconductor Future</a></li>
</ol>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Erstes eigenes Buch mit KI]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/erstes-eigenes-buch-mit-ki" />
      <id>tag:https:2025:/next-direction.de/2.29</id>
      <published>2025-03-08T11:00:00Z</published>
      <updated>2025-03-08T11:40:36Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/framework-basics.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/framework-basics.jpeg" alt="Teaser - PHP-Framework: Grundlagen"/><br>
        <p>Willkommen zurück! Heute stelle ich euch mein neuestes Projekt vor, das erste eigene Buch. Eine Warnung vorne weg, ich bin mitten im Prozess des Schreibens, es ist also nicht sicher, dass ich es bis zu Ende durchziehen werde. Ich habe auch irgendwo mal gelesen, wenn man seine Ziele offen kommuniziert, dann verpflichtet man sich in gewisser Weise auch dazu, es zu verfolgen, weil die Kontrolle von außen kommt. Wir werden sehen wie ich mich dabei anstelle.</p>

<p>Noch ist die Motivation sehr hoch, und ich freue mich jeden Tag mehr darauf, wenn der Zeitpunkt gekommen ist, dass das Buch online verfügbar ist. Der folgende Beitrag soll dabei einen Einblick in die Entstehung und Umsetzung meines ersten eigenen Buches geben.</p>

<p>Ich wünsche euch viel Spaß dabei.</p>

<h2>Einleitung</h2>

<p>Habt Ihr Euch auch schon einmal an dem Streaming-Einheitsbrei sattgesehen? Mir geht es jedenfalls so. Die Flut an Serien und Filmen, die oft nur noch Variationen desselben Themas zu sein scheinen, hat mich zunehmend gelangweilt. Und dann kommt noch der Trend hinzu, dass immer mehr Reality-TV-Formate die Streaming-Plattformen überschwemmen – für mich ein Zeichen, dass die Kreativität in der Unterhaltungsbranche langsam aber sicher auszubluten scheint. Gleichzeitig werden die Abo-Preise immer höher, und der Wert, den ich daraus ziehe, sinkt.</p>

<p>In dieser Situation habe ich mich einer neu gefundenen Leidenschaft zugewandt: dem Lesen. Bücher bieten mir nicht nur eine Flucht aus dem Alltag, sondern auch die Möglichkeit, in fremde Welten einzutauchen und Geschichten zu erleben, die oft weitaus tiefgründiger und origineller sind als das, was ich aktuell auf den Bildschirmen sehe. Doch irgendwann reichte es mir nicht mehr, nur zu konsumieren – ich wollte selbst etwas erschaffen.</p>

<p>Die Idee, ein eigenes Buch zu schreiben, hat mich schon seit Jahren begleitet. Über die Zeit, in der ich selbst unzählige Bücher gelesen habe, hat sich diese Idee immer weiterentwickelt. Was als vage Vorstellung begann, wurde zu einer konkreten Vision: eine eigene Fantasy-Welt zu erschaffen, die Leserinnen und Leser genauso fesseln könnte wie mich die Werke meiner Lieblingsautoren.</p>

<p>Doch wie geht man so ein Projekt an? Wie schafft man es, von der ersten Idee bis zum fertigen Buch zu kommen? In diesem Blogbeitrag möchte ich Euch an meiner Reise teilhaben lassen und zeigen, wie ich mit Hilfe von KI-Tools meinen Traum vom eigenen Buch verwirkliche.</p>

<h2>Grobübersicht</h2>

<p>Als ich mich über YouTube-Videos und Blogs näher mit dem Thema Buchschreiben beschäftigt habe, wurde mir schnell klar, dass der Prozess in mehrere Schritte unterteilt werden kann. Diese Schritte sind:</p>

<ol>
<li><strong>Brainstorming:</strong> Die Ideenfindung und Entwicklung der Welt.</li>
<li><strong>Outlining:</strong> Die Strukturierung der Geschichte.</li>
<li><strong>Schreiben:</strong> Der eigentliche Schreibprozess.</li>
<li><strong>Editieren:</strong> Die Überarbeitung des Textes.</li>
<li><strong>Formatieren:</strong> Die Vorbereitung des Buches für die Veröffentlichung.</li>
<li><strong>Veröffentlichen:</strong> Die Veröffentlichung des Buches auf einer Plattform.</li>
</ol>

<p>Im Folgenden werde ich jeden dieser Schritte detailliert beschreiben und Euch zeigen, welche Tools und Methoden ich verwendet habe.</p>

<h2>Schritt 1 - Brainstorming</h2>

<p>Der erste Schritt auf meiner Reise war das Brainstorming. Hier geht es darum, die Grundidee zu entwickeln und die Welt, in der die Geschichte spielt, zu erschaffen. Für diesen Prozess habe ich mich entschieden, KI-Tools zu nutzen, um meine Kreativität zu unterstützen und meine Gedanken zu strukturieren.</p>

<h3>Die Wahl des KI-Tools: Anthropics Claude 3.7</h3>

<p>Ich habe mich für Anthropics Claude 3.7 entschieden, ein KI-Modell, das speziell für kreative Aufgaben entwickelt wurde. Claude 3.7 bietet eine Funktion namens &#8220;Thinking&#8221;, die es ermöglicht, komplexe Ideen zu entwickeln und zu verfeinern. Durch die Interaktion mit der KI konnte ich meine Gedanken ordnen und neue Ideen generieren, auf die ich alleine vielleicht nicht gekommen wäre.</p>

<h3>Die Welt erschaffen: Obsidian</h3>

<p>Um meine Ideen festzuhalten und zu organisieren, habe ich Obsidian verwendet. Obsidian ist ein leistungsstarkes Tool für das Wissensmanagement, das es mir ermöglicht hat, meine Gedanken in einer strukturierten Weise zu dokumentieren. Ich habe damit begonnen, die Grundlagen meiner Fantasy-Welt zu skizzieren: Kontinente, Kulturen, Tiere, Pflanzen, Stämme und Charaktere.</p>

<p>Durch die Kombination von Claude 3.7 und Obsidian entstand nach und nach ein immer detaillierteres Bild meiner Welt. Die KI half mir dabei, Lücken zu füllen und Zusammenhänge herzustellen, während Obsidian mir die Möglichkeit gab, alles übersichtlich zu dokumentieren.</p>

<h2>Schritt 2 - Outlining</h2>

<p>Mit der geschaffenen Welt und der groben Idee der Geschichte war der nächste Schritt das Outlining. Hier geht es darum, die Handlung zu strukturieren und in Kapitel und Szenen zu unterteilen.</p>

<p>Die Struktur der Geschichte
Zusammen mit Claude 3.7 habe ich die Handlung in etwa 50 Kapitel unterteilt. Jedes Kapitel wurde dann weiter in Szenen untergliedert, um sicherzustellen, dass die Geschichte einen klaren roten Faden hat und die Spannung kontinuierlich aufrechterhalten wird.</p>

<p>Das Outlining war ein iterativer Prozess. Ich habe immer wieder Anpassungen vorgenommen, um sicherzustellen, dass die Handlung logisch ist und die Charaktere sich natürlich entwickeln. Die KI hat mir dabei geholfen, mögliche Plotlöcher zu identifizieren und alternative Handlungsstränge zu entwickeln.</p>

<h2>Schritt 3 - Schreiben</h2>

<p>Nachdem die Struktur der Geschichte feststand, konnte ich mit dem eigentlichen Schreiben beginnen. Für diesen Schritt habe ich mich für Novelcrafter entschieden, eine Plattform, die speziell für Autoren entwickelt wurde, die mit KI zusammenarbeiten möchten.</p>

<h3>Novelcrafter: Der umfassende Codex</h3>

<p>Novelcrafter ermöglicht es mir, einen umfassenden Codex zu erstellen, in dem alle Details meiner Welt dokumentiert sind. Dieser Codex dient als Referenz für die KI, die dann in der Lage ist, eine fortlaufende Geschichte zu erzählen, die konsistent mit der Welt und den Charakteren ist.</p>

<p>Der Schreibprozess mit Novelcrafter ist faszinierend. Ich gebe der KI eine grobe Richtung vor, und sie generiert Textpassagen, die ich dann überarbeiten und anpassen kann. Das Ergebnis ist eine Zusammenarbeit zwischen Mensch und Maschine, die es mir ermöglicht, schneller und effizienter zu schreiben, ohne dabei die kreative Kontrolle zu verlieren. Da ich mich dazu entschieden habe, die Geschichte auf Englisch zu erzählen, hilft es mir außerdem dabei, in einer ansprechenden Art zu kommunizieren. Obwohl ich des Englischen durch meinen Beruf und jahrelanges Schauen von Videos in Englisch mächtig bin, würde ich mir nicht zutrauen, ein Buch komplett in Englisch von der Pike auf zu schreiben.</p>

<h3>Der aktuelle Stand</h3>

<p>Aktuell befinde ich mich mitten im Schreibprozess. Es ist ein aufregender und manchmal auch herausfordernder Schritt, aber die Kombination aus meiner eigenen Kreativität und der Unterstützung durch die KI macht es zu einem einzigartigen Erlebnis.</p>

<h2>Schritt 4 - Editieren</h2>

<p>Sobald der erste Entwurf des Buches fertig ist, geht es an die Überarbeitung. Das Editieren ist ein entscheidender Schritt, um sicherzustellen, dass die Geschichte nicht nur spannend, sondern auch sprachlich und inhaltlich stimmig ist.</p>

<h3>AutoCrit: Die Analyse des Textes</h3>

<p>Für das Editieren werde ich AutoCrit verwenden, ein Tool, das speziell für Autoren entwickelt wurde, um ihre Texte zu analysieren und Verbesserungsvorschläge zu machen. AutoCrit überprüft den Text auf verschiedene Aspekte wie Wortwahl, Satzlänge, Dialoge und vieles mehr.</p>

<p>Die Vorschläge von AutoCrit werde ich dann nutzen, um den Text zu überarbeiten und zu verbessern. Das Ziel ist es, ein Buch zu schaffen, das nicht nur inhaltlich überzeugt, sondern auch sprachlich auf höchstem Niveau ist.</p>

<h3>Schritt 5 - Formatieren</h3>

<p>Nachdem das Buch inhaltlich abgeschlossen ist, muss es für die Veröffentlichung vorbereitet werden. Dazu gehört die Erstellung eines Covers und die Formatierung des Textes in die gängigen Formate wie ePub und PDF.</p>

<h3>KI-Bildgeneratoren: Das Cover erstellen</h3>

<p>Für die Gestaltung des Covers werde ich KI-Bildgeneratoren wie DALL-E oder MidJourney verwenden. Diese Tools ermöglichen es mir, ein professionelles Cover zu erstellen, das die Stimmung und den Inhalt des Buches widerspiegelt. Das ist auch einer der Punkte wo man ohne auf viel zu verzichten einiges an Geld sparen kann. Professionell erstellte Cover gehen schnell in die Richtung mehrerer hundert Euro.</p>

<h3>Die Formatierung</h3>

<p>Die Formatierung des Textes ist ein technischer Schritt, der jedoch entscheidend für die Lesbarkeit des Buches ist. Ich werde Tools wie Reedsy verwenden, um den Text in die gewünschten Formate zu konvertieren und sicherzustellen, dass das Buch auf verschiedenen Geräten gut aussieht.</p>

<h2>Schritt 6 - Veröffentlichen</h2>

<p>Der letzte Schritt auf meiner Reise ist die Veröffentlichung des Buches. Dank der modernen Technologie ist es heute einfacher denn je, ein Buch selbst zu veröffentlichen, ohne auf Verlage angewiesen zu sein.</p>

<h3>Amazon KDP: Die Plattform der Wahl</h3>

<p>Ich werde wahrscheinlich Amazon Kindle Direct Publishing (KDP) nutzen, um mein Buch zu veröffentlichen. KDP bietet eine einfache Möglichkeit, das Buch sowohl als E-Book als auch als Taschenbuch zu veröffentlichen und es einem weltweiten Publikum zugänglich zu machen.</p>

<p>Die Entscheidung für KDP ist jedoch noch nicht endgültig, da es auch andere Plattformen wie IngramSpark oder Draft2Digital gibt, die interessante Optionen bieten. Ich werde mich noch näher mit den Vor- und Nachteilen der verschiedenen Plattformen beschäftigen, bevor ich eine endgültige Entscheidung treffe. Ich werde euch über die Monate immer mal wieder darüber informieren, wie der Stand ist, und dabei dann natürlich auch erwähnen, wenn ich an bestimmten Punkte auf andere als die hier genannten Werkzeuge gesetzt habe.</p>

<h2>Fazit</h2>

<p>Die Reise von der ersten Idee bis zum fertigen Buch ist ein aufregender und lehrreicher Prozess. Die Kombination aus meiner eigenen Kreativität und der Unterstützung durch KI-Tools hat es mir ermöglicht, meinen Traum vom eigenen Buch zu verwirklichen.</p>

<p>Ich hoffe, dass dieser Blogbeitrag Euch inspiriert hat, Eure eigenen kreativen Projekte in Angriff zu nehmen. Ob Ihr nun ein Buch schreiben, ein Kunstwerk erschaffen oder ein neues Geschäft gründen wollt – die Möglichkeiten sind endlos, und die moderne Technologie steht uns dabei zur Seite.</p>

<p>Ich freue mich darauf, von Euren eigenen Projekten zu hören und vielleicht sogar eines Tages Eure Bücher zu lesen!</p>

<p>Nun will ich euch aber nicht mehr länger aufhalten. Wir lesen uns das nächste Mal auf Next Direction!</p>

<p><em>Hinweis: Die in diesem Beitrag erwähnten Tools und Plattformen sind nur Vorschläge und basieren auf meinen persönlichen Erfahrungen. Es gibt viele weitere Möglichkeiten, die je nach individuellen Bedürfnissen und Vorlieben genutzt werden können.</em></p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[RAG-System mit n8n, Supabase, Crawl4AI]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/eigenes-rag-system-mit-n8n-supabase-und-crawl4ai" />
      <id>tag:https:2025:/next-direction.de/2.28</id>
      <published>2025-02-22T09:00:00Z</published>
      <updated>2025-03-08T11:22:08Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/rag-system.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/rag-system.jpg" alt="rag-system.jpg"/><br>
        <p>Willkommen zurück! In den letzten Monaten habe ich mich intensiv mit den Problemen rund um Halluzinationen in generativen KI-Modellen und der Herausforderung der Kontextualisierung beschäftigt – Themen, die ich in meinem letzten Artikel ausführlich behandelt habe. Die Auseinandersetzung mit diesen Fragen hat mir gezeigt, dass eine robuste Kombination aus Retrieval und Generierung, also ein sogenanntes Retrieval Augmented Generation (RAG), oftmals die einzige Möglichkeit ist, um verlässliche und fachlich korrekte Ergebnisse zu erzielen. Dabei stieß ich in meinem beruflichen Alltag auf eine Anforderung, die exakt auf diese Problematik abzielt: Die Notwendigkeit, technische Dokumentationen einer umfangreichen Software präzise und aktuell darzustellen, ohne dass wichtige Zusammenhänge verloren gehen oder gar falsche Inhalte generiert werden.</p>

<p>Diese Anforderung veranlasste mich, eine maßgeschneiderte RAG-Lösung zu entwickeln, die auf modernen Tools wie n8n, Supabase und Crawl4AI basiert. In den folgenden Abschnitten möchte ich jede Komponente der Lösung detailliert vorstellen und dabei auch auf vergleichbare Projekte aus der Open-Source-Community eingehen – Projekte, die ähnliche Ansätze verfolgen, wie beispielsweise das Repository <a href="https://github.com/superagent-ai/super-rag">super-rag von superagent-ai</a> oder <a href="https://github.com/arguflow/arguflow">arguflow/arguflow</a>. Auch RAGFlow, wie es auf <a href="https://ragflow.io">ragflow.io</a> vorgestellt wird, sowie weitere Repositories wie <a href="https://github.com/philfleck/ragrug">philfleck/ragrug</a> und <a href="https://github.com/AndreasX42/RAGflow">AndreasX42/RAGflow</a> bieten spannende Einblicke in die Entwicklung und Optimierung von RAG-Systemen.</p>

<p>Diese Lösung hat ihren Ursprung in einem realen Anwendungsfall, bei dem es darum ging, umfangreiche Softwaredokumentationen zu verarbeiten, zu indexieren und in einem interaktiven Chat-anwendungsfreundlich darzustellen – ohne, dass dabei die Gefahr von „Halluzinationen“ in der generierten Ausgabe besteht. Mit Hilfe von n8n als Automatisierungswerkzeug, Supabase als skalierbare Datenbanklösung und Crawl4AI als Crawler, der relevante Datenquellen durchforstet und für den Weiterverarbeitungsprozess aufbereitet, entstand ein mehrstufiges System. Jede Komponente spielt dabei eine zentrale Rolle: Der Crawler sammelt und extrahiert Inhalte, n8n orchestriert die einzelnen Verarbeitungsschritte und Supabase stellt die Speicher- und Abfragefunktionalitäten, insbesondere durch den Einsatz von Vektorisierungstechniken, bereit. In diesem Zusammenhang ist es mir auch gelungen, den üblichen Problemen der Halluzinationsproduktion weitgehend entgegenzuwirken, da die Generierung immer auf validierte und kontextuell passende Daten zurückgreifen kann.</p>

<p>Im Folgenden gehe ich zunächst auf die Einrichtung von Crawl4AI ein, das als erster Baustein dieser Architektur dient. Ich werde detailliert beschreiben, wie ich es auf einem eigenen Server installiert habe, wie ein nginx Proxy eingerichtet wurde und welchen Stellenwert dabei ein Lets Encrypt SSL-Zertifikat hat, um eine sichere Verbindung zu garantieren. Das darauffolgende Kapitel widmet sich dem Aufbau einer RAG-Datenbank mittels eines n8n Workflows in Verbindung mit Supabase, in dem anhand einer speziellen Sitemap für die Softwaredokumentation sämtliche Inhalte intelligent gecrawlt, vektorisiert und abgespeichert werden. Den abschließenden Teil des Artikels bildet der Einsatz eines weiteren n8n Workflows zur Umsetzung eines interaktiven Chats: Hier wird die Kommunikation mit einem KI-Agenten ermöglicht, der die relevanten Daten aus Supabase abruft und sie in Form von präzisen, kontextbezogenen Antworten an den Benutzer zurückliefert. Ein besonderes Highlight ist dabei die Implementierung eines Tools, das die Metainformationen der Dokumentationsseiten als direkte Quellen verlinkt, was nicht nur Transparenz schafft, sondern auch dem Anwender die Möglichkeit bietet, tiefere Einblicke in die jeweiligen Themenfelder zu gewinnen.</p>

<p>Die Entscheidung, n8n als Workflow-Orchestrator zu wählen, basiert vor allem auf dessen Flexibilität und Erweiterbarkeit. Mit n8n lassen sich komplexe Prozesse visuell und modular darstellen, was gerade in multidisziplinären Projekten, in denen Daten aus unterschiedlichen Quellen zusammenlaufen, einen erheblichen Vorteil darstellt. Ergänzt wird dies durch Supabase, das als modernes Backend dient und dank der Möglichkeit, Vektordaten effizient zu speichern und abzufragen, eine ideale Ergänzung für den Aufbau einer Suchinfrastruktur bietet, die semantische Abfragen ermöglicht. Crawl4AI – ein speziell entwickelter Crawler – übernimmt dabei die Aufgabe, Daten aus verschiedensten Quellen aufzubereiten und gemäß den Anforderungen der Softwaredokumentation zu extrahieren. Die enge Verzahnung zwischen diesen Komponenten ermöglicht es, stets aktuelle und relevante Informationen bereitzustellen, ohne dass dabei die Gefahr einer fehlerhaften Dateninterpretation oder unerwünschter Halluzinationen besteht.</p>

<p>Die Interoperabilität zwischen den einzelnen Systemen war dabei stets eine große Herausforderung. Die verschiedenen Komponenten – n8n, Supabase und Crawl4AI – wurden ursprünglich als isolierte Werkzeuge konzipiert, mussten jedoch in diesem Projekt eng miteinander verzahnt werden, um einen nahtlosen Informationsfluss zu ermöglichen. Daher war es essenziell, über ein exzellentes Fehler- und Ausnahmehandling zu verfügen, das mögliche Inkonsistenzen oder Verzögerungen frühzeitig erkennt und behebt. Mit den oben erwähnten Tools konnte eine skalierbare und ausfallsichere Lösung realisiert werden, die sich auch in produktiven Umgebungen mühelos bewährt. Die dabei gewonnenen Erfahrungen und Erkenntnisse sind nicht nur für den konkreten Anwendungsfall von unschätzbarem Wert, sondern bieten auch einen interessanten Ausgangspunkt für zukünftige Projekte, bei denen eine hohe Datenqualität und Zuverlässigkeit von zentraler Bedeutung sind.</p>

<p>Die Relevanz der Thematik und die Vielschichtigkeit der Herausforderung macht deutlich, warum der Aufbau eines stabilen RAG-Systems gerade jetzt von so großer Bedeutung ist. Mit dem Siegeszug generativer KI-Systeme stehen Unternehmen und Entwickler vor der Aufgabe, die Balance zwischen Kreativität und Faktenstreue zu finden – ein Balanceakt, der in meinem beruflichen Alltag unmittelbar spürbar ist. Insgesamt hat mich diese Projektarbeit nicht nur in technischer Hinsicht bereichert, sondern mir auch gezeigt, wie wichtig es ist, bei der Entwicklung von KI-Systemen immer einen Schritt voraus zu denken und bestehende Methoden kontinuierlich weiterzuentwickeln.</p>

<p>Im Vergleich zu Ansätzen, die ausschließlich auf generative Modelle setzen, hat meine Lösung den Vorteil, dass sie auf einem soliden Fundament verifizierter Daten aufbaut. Dies ist der Schlüssel zur Minimierung von Fehlern und zur Steigerung der Gesamtzuverlässigkeit des Systems. Letztlich zeigt sich, dass die enge Verzahnung von Crawler-Technologien, Workflow-Automatisierung und moderner Datenbankarchitektur zu einer signifikanten Verbesserung der Systemstabilität und Datenintegrität führt – ein Befund, der in der aktuellen Forschung und Praxis gleichermaßen Bedeutung findet.</p>

<p>Im Folgenden soll nun die technische Umsetzung im Detail vorgestellt werden, um euch nicht nur theoretische Einblicke, sondern auch praktische Implementierungsbeispiele und Konfigurationsdateien an die Hand zu geben. Es folgt der erste Bestandteil des Systems: Die Implementierung von Crawl4AI auf einem eigenen Server.</p>

<h2>Teil 1: Crawl4AI – Einrichtung und Konfiguration auf dem Eigenen Server</h2>

<p>In diesem ersten technischen Abschnitt widme ich mich der Einrichtung und dem Betrieb von Crawl4AI, einer leistungsfähigen Komponente zur automatisierten Erfassung und Aufbereitung von Webseiteninhalten. Ziel dieser Komponente war es, die umfangreichen Informationen der Softwaredokumentation systematisch zu erfassen, zu parsen und in einem für weiterführende Prozesse optimal nutzbaren Format bereitzustellen. Dabei spielt die Übertragung der Daten in eine zentrale Datenbank eine tragende Rolle – insbesondere in Hinblick auf die spätere Vektorisierung und Integration in einen RAG-Workflow.</p>

<p>Die Implementierung von Crawl4AI auf einem eigenen Server bringt zahlreiche Herausforderungen mit sich, angefangen von der Einrichtung einer stabilen Hosting-Umgebung über die Konfiguration eines Reverse Proxys bis hin zur Einrichtung eines SSL-Zertifikats, das für eine sichere Verschlüsselung sämtlicher Datenströme sorgt. Ein zentraler Bestandteil dieser Umgebung ist der Einsatz von Docker Compose, der es ermöglicht, alle benötigten Dienste in Form von Containern zu orchestrieren und so eine konsistente sowie reproduzierbare Infrastruktur zu gewährleisten.</p>

<h3>Technische Grundlagen und Systemarchitektur</h3>

<p>Der erste Schritt bestand darin, den eigenen Server zu konfigurieren und alle notwendigen Softwarekomponenten zu installieren. Dabei wurde besonderes Augenmerk auf Stabilität, Skalierbarkeit und Sicherheit gelegt. Das grundlegende Systemsetup beinhaltete eine aktuelle Linux-Distribution, wobei Ubuntu 22.04 LTS bevorzugt wurde, da es eine langfristige Support-Version darstellt und sich hervorragend für produktionsreife Umgebungen eignet. Neben der Basisinstallation wurden alle sicherheitsrelevanten Updates durchgeführt und der Server für den produktiven Einsatz gehärtet.</p>

<p>Die gesamte Systemarchitektur gliedert sich in mehrere Ebenen. An oberster Stelle steht dabei der Webzugang über einen nginx Reverse Proxy, der eigens konfiguriert wurde, um den Traffic optimal zu verteilen, Anfragen zu terminieren und gleichzeitig als SSL-Terminator zu agieren. Hierzu kam Lets Encrypt zum Einsatz, das kostenfrei ausstellbare SSL-Zertifikate bereitstellt. Die Kombination aus nginx und Lets Encrypt sichert nicht nur den Datenverkehr, sondern ermöglicht auch eine einfache und automatisierte Erneuerung der Zertifikate, was gerade in produktiven Systemen von unschätzbarem Wert ist.</p>

<p>Einer der größten Vorteile der gewählten Architektur liegt in der Modularität: Alle Dienste – vom Webserver über den Reverse Proxy bis hin zu den einzelnen Containern, die Crawl4AI und damit verbundene Anwendungen hosten – werden über Docker Compose gesteuert. Dieser Ansatz erlaubt es, die gesamte Infrastruktur als Code zu verwalten, Änderungen reproduzierbar zu machen und auch in einer Continuous-Deployment-Pipeline zu integrieren.</p>

<h3>Einrichtung des nginx Reverse Proxys und Lets Encrypt Zertifikats</h3>

<p>Die Konfiguration des nginx Reverse Proxys stellte einen kritischen Punkt im Setup dar. Ziel war es, den Zugang zu den internen Docker-Containern sicher zu regeln und dabei die Flexibilität einer Container-basierten Infrastruktur zu bewahren. In der nginx-Konfiguration wurden mehrere Server-Blöcke definiert, die jeweils spezifische Subdomains für die einzelnen Dienste abbilden sollten. Dabei musste darauf geachtet werden, dass der interne Datenverkehr nur über sichere Kanäle erfolgt – daher wurde für jeden zugänglichen Dienst ein SSL-Zertifikat mittels Lets Encrypt eingerichtet.</p>

<p>Die Konfiguration sah in etwa folgendermaßen aus:</p>

<pre><code class="nginx">server &#123;

    server_name crawl.my-domain.de;

    location / &#123;
        proxy_pass http://localhost:11235;
        include proxy_params;
    &#125;

    listen &#91;::&#93;:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/crawl.my-domain.de/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/crawl.my-domain.de/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
&#125;

server &#123;
    if ($host = crawl.my-domain.de) &#123;
        return 301 https://$host$request_uri;
    &#125; # managed by Certbot

    listen 80;
    listen &#91;::&#93;:80;

    server_name crawl.my-domain.de;
    return 404; # managed by Certbot
&#125;
</code></pre>

<p>Diese Konfiguration stellt sicher, dass alle Anfragen über HTTPS laufen und die interne Kommunikation an den entsprechenden Container weitergeleitet wird. Neben dem grundlegenden Setup wurde auch ein automatisierter Prozess zur Zertifikatserneuerung eingerichtet, um administrativen Aufwand zu minimieren und Sicherheit kontinuierlich zu gewährleisten.</p>

<h3>Herausforderungen im Betrieb und Lösungsansätze</h3>

<p>Die Einrichtung eines solch komplexen Setup brachte naturgemäß auch einige Herausforderungen mit sich. Einer der ersten Kritikpunkte war die Synchronisation der Container untereinander. Bei Docker Compose ist das wichtigste das Port-Mapping. Hier solltet ihr darauf achten, wenn ihr mehrere Projekte auf dem gleichen Server laufen habt, dass diese sich nicht im Mapping überschneiden. In diesem Beispiel war das kein Problem, da ein eher exotischer Port verwendet wurde. Außerdem ein wichtiger Punkt sind in der Regel die definierten Volumes, um Datenverlust zu vermeiden, wenn man die Container mal stoppen muss. Da Crawl4AI ein eher zustandsloses System ohne Benutzernamen ist, kann hier das Volume im Arbeitsspeicher gehalten werden.</p>

<pre><code class="yaml">version: '3.8'
services:
  crawl4ai:
    image: unclecode/crawl4ai:all-amd64
    ports:
      - "11235:11235"
    environment:
      - CRAWL4AI_API_TOKEN=$&#123;CRAWL4AI_API_TOKEN:-&#125;  # Optional API security
      - MAX_CONCURRENT_TASKS=10
      # LLM Provider Keys
      - OPENAI_API_KEY=$&#123;OPENAI_API_KEY:-&#125;
      - ANTHROPIC_API_KEY=$&#123;ANTHROPIC_API_KEY:-&#125;
    volumes:
      - /dev/shm:/dev/shm
    deploy:
      resources:
        limits:
          memory: 8G
        reservations:
          memory: 2G
</code></pre>

<p>Weiterhin musste ich darauf achten, dass das System auch bei hoher Last stabil bleibt. Gerade der Crawler, der potenziell sehr viele Anfragen in kurzer Zeit verarbeiten muss, benötigte eine sorgfältige Konfiguration von Ressourcen. Es wurden Limits bezüglich Speicher und CPU-Ressourcen in Docker konfiguriert, sodass einzelne Container nicht den Gesamtbetrieb beeinträchtigen können. Gleichzeitig bot die Möglichkeit, Container horizontal zu skalieren, einen zusätzlichen Puffer gegen Lastspitzen.</p>

<h3>Integration von Crawl4AI in den Gesamtworkflow</h3>

<p>Nachdem die Basisinfrastruktur stand, erfolgte der nächste Schritt: Die Integration von Crawl4AI in den umfassenden Workflow zur Erstellung der RAG-Lösung. Crawl4AI ist dafür konzipiert, Webseiteninhalte automatisiert zu erfassen und in ein strukturiertes Format zu überführen. Der Crawler wurde dabei über die REST-API von den entsprechenden n8n-Knoten angesprochen. Die Datenquelle war dabei eine Sitemap der gewünschten Dokumentation, diese wurde in einer Schleife durchgelaufen, und jede Unterseite entsprechend gecrawlt. Dabei war etwas tüfteln angesagt, so dass z.B. nicht auf jeder Seite ein Glossar enthalten war, was die Genauigkeit der Suche natürlich verschlechterte. Außerdem habe ich über einen CSS-Selektor nur den Seiteninhalt gescannt, der auch wirklich relevant war. Das hilft auch den Speicherverbrauch in der Datenbank stark zu reduzieren.</p>

<h3>Fazit Crawl4AI</h3>

<p>Die Einrichtung von Crawl4AI auf einem eigenen Server erwies sich als ein anspruchsvoller, aber letztlich sehr lohnender Prozess. Durch den gezielten Einsatz moderner Technologien wie Docker Compose, nginx als Reverse Proxy und Lets Encrypt für den sicheren Betrieb konnte eine robuste, skalierbare Plattform geschaffen werden, die den Grundstein für den weiteren Aufbau eines leistungsfähigen RAG-Systems legt. Alle Komponenten arbeiten hier eng zusammen, um die Inhalte der Softwaredokumentation automatisiert zu erfassen, aufzubereiten und für die nachfolgenden Workflows verfügbar zu machen. Diese technische Basis bildet somit die Grundlage für die nächsten Schritte, in denen die gesammelten Daten in einem intelligenten n8n Workflow weiterverarbeitet und in eine Vektor-Datenbank integriert werden.</p>

<p>Hier das fertige Ergebnis aus n8n:</p>

<p><img src="/images/uploads/crawler.jpg" alt="ComfyUI Beispiel-Bild" /></p>

<h2>Teil 2: n8n Workflow für Aufbau einer RAG Datenbank mit Supabase Vektor Knoten</h2>

<p>Nachdem die Sammlung und Aufbereitung der Daten mittels Crawl4AI und dem stabilen Hosting-Setup erfolgreich implementiert wurde, besteht der nächste logische Schritt darin, die gewonnenen Inhalte in einer strukturierten und durchsuchbaren Form abzuspeichern. Hierfür kommt ein n8n-Workflow zum Einsatz, der die Daten aus Crawl4AI automatisch weiterverarbeitet, vektorisiert und in eine Supabase-Datenbank einspeist. Dieser Abschnitt beleuchtet detailliert den gesamten Prozessablauf, beginnend mit der Erstellung einer Sitemap, über das automatisierte Crawlen jeder einzelnen Seite bis hin zur abschließenden Vektorisierung und Speicherung in Supabase.</p>

<p>Die Grundidee hinter diesem Ansatz ist es, eine robuste Datenbasis zu schaffen, die als Fundament für die Retrieval Augmented Generation dient. Durch die Vektorisierung der Texte – also die Umwandlung von semantischen Inhalten in numerische Repräsentationen – können spätere Suchanfragen und semantische Abgleiche äußerst präzise und performant durchgeführt werden. Dies bildet die Basis für den nachfolgenden Chat-Workflow, bei dem die relevanten Dokumentationsinhalte in Echtzeit abgerufen und dem Anwender in Form von KI-generierten Antworten präsentiert werden.</p>

<h3>Sitemap einer Softwaredokumentation</h3>

<p>Der Ausgangspunkt dieses Workflows ist die Sitemap der Softwaredokumentation. Eine präzise strukturierte Sitemap stellt sicher, dass alle relevanten Seiten erfasst werden und keine Inhalte übersehen werden. Die Sitemap dient dabei als Masterliste, die den Grundstock für den anschließenden Crawling-Prozess legt. Sie bietet außerdem die Möglichkeit unerwünschte Seiten, wie in meinem Fall den Glossar, auszuschließen.</p>

<p>Die Erstellung der Sitemap habe ich mit einem Tool im Internet durchgeführt, da die Dokumentation der gewünschten Software nicht direkt etwas lieferte. Diese habe ich anschließend auf einer meiner Domains gehostet, um sie von n8n einlesen zu lassen.</p>

<h3>Jede Seite per Crawl4AI gecrawled</h3>

<p>Nachdem die Sitemap erstellt und strukturiert vorlag, übernahm Crawl4AI die Aufgabe, jede einzelne Seite zu crawlen. Hierbei wurden sämtliche Inhalte der Softwaredokumentation automatisiert ausgelesen und in einem einheitlichen Format abgespeichert. Dieser Schritt war von entscheidender Bedeutung, da er die Rohdaten liefert, auf denen im nächsten Schritt die Vektorisierung basiert.</p>

<p>Der Crawling-Prozess musste dabei einiges an Flexibilität aufweisen: Unterschiedliche Seiten enthalten variierende HTML-Strukturen, eingebettete Medien und dynamisch generierte Inhalte. Um diesen Herausforderungen gerecht zu werden, wurde Crawl4AI so konfiguriert, dass es mittels integrierter Selektoren die relevanten Textblöcke extrahiert und irrelevante oder störende Elemente wie Navigationselemente oder Werbung herausfiltert. Ein iterativer Verbesserungsprozess sorgte dafür, dass auch spezielle Seitenformate zuverlässig verarbeitet werden konnten.</p>

<p>Hierbei kam auch fortschrittliche Logik zum Einsatz: Für jede gecrawlte Seite wurde zunächst die URL, der gesamte HTML-Quellcode sowie extrahierte Metainformationen (wie Titel, Veröffentlichungsdatum und Autor) gespeichert. Diese Metadaten dienten später als Basis für die Verknüpfung der Inhalte mit weiteren Informationen in der Datenbank. Ein besonderer Fokus lag dabei auf der Konsistenz und Reproduzierbarkeit der Crawl-Ergebnisse – essenziell, um spätere Abfragen korrekt und präzise durchführen zu können.</p>

<h3>Speicherung nach Vektorisierung in Supabase</h3>

<p>Der finale Schritt dieses Workflows bestand in der Vektorisierung der crawled Inhalte und deren Speicherung in einer Supabase-Datenbank. Die Vektorisierung erfolgte mittels eines Machine-Learning-Modells, das die semantischen Inhalte der Texte in numerische Vektoren umwandelt. Diese Vektoren ermöglichen anschließend durch mathematische Abgleiche eine präzise Ermittlung semantischer Ähnlichkeiten zwischen verschiedenen Dokumenten. In meinem Fall habe ich ein Modell von OpenAI verwendet, da es vermutlich eines der Ausgereiftesten ist, und so sehr gute Ergebnisse erzielt werden konnten.</p>

<p>Supabase erwies sich hierbei als ideale Lösung, da es als skalierbare, moderne Datenbank nicht nur klassische SQL-Abfragen unterstützt, sondern auch nativ Anbindungen und Erweiterungen für Vektor-basierte Suchen bietet. Die Daten, bestehend aus einem Originaltext, seinen Metainformationen und den zugehörigen Vektor-Repräsentationen, wurden in einer relationalen Datenbank abgelegt und konnten dadurch bei Bedarf schnell und effizient abgefragt werden. Dieser Ansatz wird auch in anderen Projekten wie <a href="https://github.com/superagent-ai/super-rag">super-rag von superagent-ai</a> propagiert – dort wird der Mehrwert von Caching und performanten Vektor-Suchabfragen hervorgehoben, um dynamische KI-Anwendungen zu unterstützen.</p>

<p>Der gesamte Vektorisierungsprozess wurde in den n8n Workflow integriert. Sobald Crawl4AI eine Seite erfolgreich gecrawlt hatte, wurde der Text an einen Vektorisierungs-Service weitergeleitet, der das Modell auf die Inhalte anwendete. Die erzeugten Vektoren wurden dann zusammen mit den ursprünglichen Metadaten in Supabase gespeichert. Diese Integration erfolgte über eine RESTful API, wobei alle relevanten Datenfelder (einschließlich der URL, des Titels, des generierten Vektors und weiterer Indexinformationen) in der Datenbank angelegt wurden.</p>

<p>Dank dieses durchdachten Workflows kann nun jede Abfrage, die im späteren Chat-Modul ausgeführt wird, auf die umfangreichen Vektor-Daten zugreifen und kontextuell passende Ergebnisse zurückliefern. Die Vektor-Suche erlaubt es, selbst bei sehr großen Datenmengen schnelle und präzise Suchergebnisse zu erzielen, was den Anwendern in der Praxis einen bedeutenden Mehrwert bietet. Durch den direkten Bezug zu den Metainformationen, die in diesem Schritt ebenfalls erfasst wurden, lassen sich zudem Links zu den entsprechenden Dokumentationsseiten generieren, wodurch Transparenz und Nachvollziehbarkeit erhöht werden.</p>

<p>Zusammenfassend lässt sich sagen, dass die Integration der Daten aus Crawl4AI in eine Vektor-Datenbank via n8n Workflow die Basis für die spätere Retrieval-Augmented Generation bildet. Mit einem soliden Fundament an verarbeiteten und semantisch indexierten Inhalten kann der nächste Schritt – der Chat-Workflow – effizient realisiert werden. Diese eng gekuppelte Kette aus Datenaufbereitung, Vektorisierung und Speicherung stellt die Voraussetzung dar, um auf Grundlage der originalen Softwaredokumentation verlässliche, kontextbezogene Antworten zu generieren.</p>

<h2>Teil 3: n8n Workflow für Chat mit RAG Daten über Supabase Vektor Tool und AI Agent</h2>

<p>Nachdem sämtliche Inhalte der Softwaredokumentation systematisch erfasst, verarbeitet, vektorisiert und in Supabase abgespeichert wurden, widmet sich der finale Teil des Projekts der Umsetzung eines interaktiven Chats. Ziel dieses Workflows ist es, Anfragen von Anwendern in Echtzeit zu verarbeiten, relevante Dokumentationsinhalte aus der Datenbank abzurufen und diese als kontextuell passende, KI-generierte Antworten zurückzuliefern. Der Aufbau dieses Chat-Systems basiert wieder auf der leistungsstarken Workflow-Automatisierung von n8n und integriert einen speziell entwickelten KI-Agenten, der mittels eines integrierten Tools in Supabase Abfragen ausführt.</p>

<p>Im Detail gliedert sich dieser Chat-Workflow in mehrere Schritte: Zunächst erfolgt ein einfacher Chat-Auslöser, der Anfragen der Nutzer aufnimmt und in den Workflow einspeist. Anschließend wird ein AI Agent aktiviert, der – unterstützt durch das Supabase Vektor Tool – die relevanten Vektor-Daten abruft und analysiert. Ein besonderes Highlight dieses Systems stellt die Verknüpfung der generierten Antworten mit expliziten Links zu den zugrundeliegenden Dokumentationsseiten dar, die aus den Metainformationen hervorgehen. Diese Vorgehensweise gewährleistet, dass der Anwender stets nachvollziehen kann, auf welche Quelle sich die Antwort bezieht.</p>

<h3>Einfacher Chat-Auslöser</h3>

<p>Der erste Bestandteil des Chat-Workflows ist ein simpler, aber äußerst robuster Chat-Auslöser. Hierbei handelt es sich um einen n8n Node, der Anfragen von Nutzern entgegennimmt – sei es über ein Frontend-Widget, einen API-Endpunkt oder sogar eine Slack-Integration. Der Chat-Auslöser nimmt den rohen Text der Anfrage entgegen und leitet diesen direkt an weitere Verarbeitungsschritte weiter. Dabei wird auch der Kontext der Anfrage, beispielsweise die Identifikation des Nutzers und zusätzliche Metadaten, mit übermittelt, sodass der nachfolgende AI Agent alle relevanten Informationen vorliegen hat.</p>

<p>Technisch erfolgt dieser Schritt über einen HTTP-Request-Trigger in n8n. Sobald eine HTTP-Anfrage eintrifft, wird automatisch ein neuer Workflow-Job gestartet, der die Anfrage verarbeitet und sofort erste Verarbeitungslogiken anstößt. Durch die Nutzung von n8n können auch Rückmeldungen in Echtzeit an den Anwender erfolgen – beispielsweise ein kurzes „Bitte warten, Ihre Anfrage wird bearbeitet …“. Diese Rückkopplung erhöht die Nutzerzufriedenheit und schafft Vertrauen in den Chat-Service.</p>

<h3>AI Agent mit Tool um Supabase abzufragen</h3>

<p>Im Anschluss an den ersten Trigger kommt der AI Agent zum Einsatz. Dieser Agent übernimmt die semantische Analyse der Eingabedaten und führt eine gezielte Abfrage in der Supabase-Datenbank durch. Ausgestattet mit einem speziell entworfenen Tool, das auf den vektorisierten Daten basiert, sucht der AI Agent nach den dokumentationsrelevanten Inhalten, die am besten zur Nutzeranfrage passen. Dabei spielt vor allem die Fähigkeit zur semantischen Ähnlichkeitssuche eine entscheidende Rolle, denn nur so lässt sich gewährleisten, dass die zurückgelieferten Inhalte auch inhaltlich stimmig sind.</p>

<p>Der AI Agent basiert auf einem hybriden Modell, das klassische Abfrageverfahren mit modernen NLP-Techniken kombiniert. Zunächst wird die Anfrage in einen Vektor überführt, der dann mit den bereits in Supabase gespeicherten Vektor-Repräsentationen verglichen wird. Durch mathematische Distanzberechnungen, wie etwa die Kosinusähnlichkeit, wird der am besten passende Textabschnitt identifiziert. Die Verwendung eines solchen Ansatzes minimiert typische Risiken von Halluzinationen, da die Antwort nicht rein generativ erzeugt, sondern durch einen direkten Bezug zu validierten Dokumentationsinhalten gestützt wird. Die technologische Umsetzung dieses Ansatzes erinnert an Methoden, die auch in anderen Projekten wie <a href="https://github.com/superagent-ai/super-rag">super-rag von superagent-ai</a> und <a href="https://github.com/arguflow/arguflow">arguflow/arguflow</a> zum Einsatz kommen.</p>

<h3>Link zu Dokumentationsseiten aus Metainformationen</h3>

<p>Ein besonderes Feature des Chat-Workflows ist die Generierung und Integration von direkten Links zu den originalen Dokumentationsseiten. Nachdem der AI Agent die passenden Inhalte identifiziert hat, werden Meta-Daten, wie z. B. die URL der jeweiligen Seite und zusätzliche Beschreibungen, in die Chat-Antwort integriert. Dadurch erhält der Anwender nicht nur eine fundierte, KI-generierte Antwort, sondern auch die Möglichkeit, durch Anklicken des Links vertiefte Informationen direkt aus der Quell-Dokumentation zu erhalten. Dies erhöht nicht nur die Transparenz, sondern fördert auch das Vertrauen in die Richtigkeit und Aktualität der generierten Informationen.</p>

<p>Die technische Umsetzung dieses Features erfolgt über einen weiteren n8n Node, der nach der Abfrage des AI Agents die zugehörigen Metainformationen aus der Datenbank abruft und diese strukturiert in die Antwort einbettet. Dabei werden etwaige Duplikate bereinigt und die Links in einem gut lesbaren Format ausgegeben, sodass sie unmittelbar als Klickbare Verweise erkennbar sind. Dies hat den zusätzlichen Vorteil, dass bei Rückfragen oder Unklarheiten sofort die Originalquelle herangezogen werden kann – ein essenzieller Qualitätsfaktor in technischen Dokumentationen.</p>

<h3>Gesamtbetrachtung des Chat-Workflows</h3>

<p>Zusammenfassend stellt der Chat-Workflow den krönenden Abschluss des gesamten Projekts dar. Durch die konsequente Kombination von einem einfachen Trigger, einem intelligenten AI Agenten und der Integration von Meta-Informationen wird eine Kommunikationsschicht geschaffen, die sowohl technisch anspruchsvoll als auch nutzerfreundlich ist. Es gelingt, eine Brücke zu schlagen zwischen der komplexen Backend-Logik der Vektorisierung und der intuitiven Interaktion mit dem Endanwender. Dieser mehrschichtige Ansatz stellt sicher, dass Anfragen nicht nur schnell und präzise beantwortet werden, sondern auch stets auf verifizierte Daten zurückgreifen – ein entscheidender Vorteil gegenüber rein generativen Ansätzen, die oft von Halluzinationen und fehlender Quellenbezogenheit geprägt sind.</p>

<p>Die Erkenntnisse aus den bisherigen Schritten – die zuverlässige Datenaufbereitung mittels Crawl4AI, die durchdachte Vektorisierung in Supabase und die flexible Workflow-Automatisierung in n8n – fließen hier zusammen, um ein System zu schaffen, das in der Lage ist, die Herausforderungen moderner Softwaredokumentationen zu meistern. Die kontinuierliche Optimierung der einzelnen Komponenten steht dabei im Fokus, sodass auch zukünftige Erweiterungen und Anpassungen problemlos implementiert werden können. Dieser iterative Verbesserungsprozess ist essenziell, um den ständig wachsenden Anforderungen im Zuge der Digitalisierung und fortschreitender KI-Integration gerecht zu werden.</p>

<p>Hier der Chat-Workflow aus n8n:</p>

<p><img src="/images/uploads/help-bot.jpg" alt="ComfyUI Beispiel-Bild" /></p>

<h2>Zusammenfassung</h2>

<p>In der abschließenden Betrachtung des Projekts kann ich mit Gutem Gewissen feststellen, dass die entwickelte RAG-Lösung in vielerlei Hinsicht einen entscheidenden Schritt nach vorne darstellt. Die ursprüngliche Herausforderung – die Problematik der Halluzinationen bei generativen KI-Modellen – wurde nahezu vollständig gelöst. Durch die Kombination eines robusten Retrieval-Mechanismus mit einem leistungsstarken generativen Modell konnte ich erreichen, dass alle Antworten auf validierte und kontextuell passende Daten gründen. Dies ist insbesondere in der Umgebung einer komplexen Softwaredokumentation von unschätzbarem Wert, da hier Genauigkeit und Verlässlichkeit oberste Priorität besitzen. Der Rest wird durch entsprechendes Prompting des Chat-Agenten erreicht. Mit etwas rumprobieren habe ich diesen dann auch soweit gebracht, dass er bei Unwissenheit nicht wieder ins Geschichtenerzählen übergeht.</p>

<p>Die daraus resultierende RAG-Lösung ist somit nicht nur eine technische Meisterleistung, sondern gleichzeitig ein praxisnaher Prototyp, der den konkreten Anforderungen im beruflichen Alltag gerecht wird. Durch die iterative Verfeinerung und die konsequente Einbindung moderner Technologien konnte ein System geschaffen werden, das als Referenzmodell für zukünftige Projekte in diesem Bereich dienen kann. Die Möglichkeit, dynamisch auf aktuelle Dokumentationen zuzugreifen, während gleichzeitig das Risiko von Halluzinationen minimiert wird, eröffnet völlig neue Perspektiven in der Verarbeitung und Präsentation technischer Inhalte.</p>

<p>Diese Lösung repräsentiert einen Paradigmenwechsel in der Art und Weise, wie KI-basierte Systeme mit komplexen Datenstrukturen umgehen. Kein System bleibt statisch – kontinuierliche Updates und Optimierungen sind notwendig, um den hohen Ansprüchen moderner Softwarelösungen gerecht zu werden. In meinem Fall war es besonders bereichernd zu sehen, wie sich theoretische Überlegungen und experimentelle Ansätze in eine funktionierende Praxislösung übersetzen ließen.</p>

<p>Die Erfahrungen, die ich im Laufe dieses Projekts sammeln konnte, sind ein Beleg für die technische und methodische Reife, die erforderlich ist, um in der schnelllebigen Welt der KI stets einen Schritt voraus zu sein. Die enge Kooperation der verschiedenen Systemkomponenten und die ständige Optimierung der Arbeitsabläufe tragen dazu bei, dass die Gesamtarchitektur nicht nur stabil läuft, sondern auch flexibel erweiterbar ist. In einer Zeit, in der technologische Entwicklungen in rasantem Tempo voranschreiten, ist es essenziell, Systeme zu entwickeln, die sich nahtlos in bestehende Arbeitsprozesse integrieren lassen und gleichzeitig den höchsten Ansprüchen an Sicherheit und Performance genügen.</p>

<p>Ich hoffe, dass dieser tiefe Einblick in die technische Umsetzung und die integrierten Komponenten eurer Neugierde gerecht wird und als Inspiration für eigene Projekte dient.</p>

<p>Jetzt will ich euch aber nicht länger aufhalten, der Artikel war schon lange genug. Wir lesen uns das nächste Mal auf Next Direction!</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[KI-Halluzinationen und Lösung mittels RAG]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/ki-halluzinationen-und-loesung-mittels-rag" />
      <id>tag:https:2025:/next-direction.de/2.27</id>
      <published>2025-02-15T19:00:00Z</published>
      <updated>2025-02-15T19:36:37Z</updated>
      <author>
            <name>ndn8n-r</name>
            <email>info@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/RAG-AI.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/RAG-AI.jpg" alt="RAG-AI"/><br>
        <p>Willkommen zurück! Heute geht es um Halluzinationen von KI-Modellen. Dieses Thema ist von entscheidender Bedeutung wenn man darüber nachdenkt, was manche Menschen für Hoffnungen in aktuelle Modelle setzen. Es gibt einige Kritiker die sagen, in der aktuellen Form wird KI nie den Stand von Menschen erreichen, da wir einfach anders denken und lernen als derzeitige Modelle.</p>

<p>Wer es nicht weiß, dem will ich es hier nochmal mit auf den Weg geben. Derzeitige KI-Modelle generieren die Antworten mit simplen Wortvorhersagen. Mit euren Prompts gebt ihr einen Startpunkt vor und das jeweilige Modell hat dann mehr oder weniger Erfahrung in diesem Bereich durch Training gesammelt, und kann euch so mehr oder weniger zufriedenstellende Vorhersagen geben. Zu keiner Zeit &#8220;denken&#8221; heutige Modelle wirklich über irgendetwas nach, oder kommen gar auf eigene Ideen!</p>

<p>Mit diesem Wissen wünsche ich euch viel Spaß beim folgenden Beitrag.</p>

<hr />

<p>In den letzten Jahren hat die Künstliche Intelligenz (KI) bemerkenswerte Fortschritte erzielt, doch das Phänomen der KI-Halluzinationen bleibt eine bedeutende Herausforderung. Diese Halluzinationen, die zu fehlerhaften oder nicht realen Inhalten führen, sind oft das Resultat unzureichender Daten und mangelhafter Modellverständnisse. Gleichzeitig präsentiert die Retrieval-Augmented Generation (RAG) innovative Ansätze zur Verbesserung der Zuverlässigkeit von KI-Systemen. In diesem Artikel werden die Ursachen von KI-Halluzinationen sowie die Potenziale von RAG eingehend untersucht, um ein umfassendes Verständnis für die aktuellen Entwicklungen im Bereich der Künstlichen Intelligenz zu bieten.</p>

<h2>Einführung in KI und ihre Herausforderungen</h2>

<p>Künstliche Intelligenz (KI) bezeichnet die Fähigkeit von Maschinen, insbesondere Computersystemen, Aufgaben auszuführen, die typischerweise menschliche Intelligenz erfordern. Zu den wesentlichen Aspekten von KI gehören das logische Denken, Problemlösung, Lernen und Entscheidungsfindung. KI-Systeme nutzen Algorithmen und rechnergestützte Verfahren, um große Datenmengen zu verarbeiten, Muster zu extrahieren und Vorhersagen oder Entscheidungen basierend auf diesen Mustern zu treffen. Eine Unterkategorie der KI, die generative KI, konzentriert sich darauf, Inhalte wie Text, Bilder, Videos und Musik als Reaktion auf Benutzereingaben zu erstellen [1].</p>

<p>Eine der bedeutendsten Herausforderungen in der KI, insbesondere bei großen Sprachmodellen (LLMs), ist das Auftreten von Halluzinationen. KI-Halluzinationen sind Fälle, in denen KI-Systeme inkorrekte oder irreführende Informationen generieren, die nicht mit ihren Trainingsdaten, Eingabeaufforderungen oder den erwarteten Ergebnissen übereinstimmen. Diese Halluzinationen können sich in Form von falschen Fakten, unrealistischen Bildern oder unsinnigem Text äußern [2].</p>

<p>Die Ursachen für KI-Halluzinationen sind vielfältig. Erstens kann unzureichendes oder verfälschtes Trainingsmaterial eine erhebliche Rolle spielen. Wenn ein KI-Modell nicht mit ausreichend qualitativ hochwertigen Daten trainiert wird, kann dies zu verzerrten oder fehlerhaften Ergebnissen führen. Zweitens kann Überanpassung an die Trainingsdaten auftreten, wodurch das Modell zwar sehr gut im Umgang mit den gelernten Daten wird, jedoch weniger fähig ist, auf neue oder unbekannte Daten adäquat zu reagieren [3].</p>

<p>Die negativen Auswirkungen von Halluzinationen können in kritischen Bereichen wie dem Gesundheitswesen gravierend sein. Fehlerhafte Diagnosen oder Behandlungsempfehlungen, die auf halluzinierten Daten basieren, können die Patientensicherheit gefährden. Um diesen Herausforderungen zu begegnen, setzen Forscher und Entwickler auf verschiedene Methoden, darunter die Verwendung hochqualitativer Trainingsdaten, regelmäßige Tests und die Anwendung von kontinuierlicher Optimierung. Dennoch bleibt die vollständige Eliminierung von Halluzinationen eine komplexe Ingenieursaufgabe, die fortlaufend angegangen werden muss [4].</p>

<hr />

<h2>Ursachen für KI-Halluzinationen</h2>

<p>KI-Halluzinationen ergeben sich häufig aus einer Kombination von unzureichenden Daten, Modellüberanpassung und unklaren Eingabeaufforderungen. Ein zentrales Problem ist die Verwendung von Trainingsdaten, die entweder lückenhaft oder verzerrt sind. Wenn KI-Modelle mit unzureichenden oder unausgewogenen Datensätzen trainiert werden, führen die daraus resultierenden Ausgaben häufig zu Fehlinformationen oder Missverständnissen. Solche Halluzinationen können schweren Schaden anrichten, insbesondere im medizinischen oder juristischen Bereich, wo präzise individuelle Informationen unabdingbar sind. Ein Beispiel hierfür ist die Anwendung von KI in der medizinischen Diagnostik, wo falsche Ergebnisse ernsthafte Konsequenzen für die Patientenversorgung haben können [5].</p>

<p>Ein weiterer bedeutender Faktor ist die Überanpassung (Overfitting) von Modellen an ihre Trainingsdaten. Dies geschieht, wenn das Modell so stark auf die spezifischen Eingabedaten trainiert wird, dass es Schwierigkeiten hat, in neuen, nicht erlernten Szenarien zu verallgemeinern. Dadurch entstehen Situationen, in denen das Modell plausible, jedoch falsche Informationen generiert, die es nicht erlernen konnte. Ein praktisches Beispiel wäre ein Sprachmodell, das aufgrund einer übermäßigen Anpassung an spezifische Stilblüten oder Datenpunkte inkonsistente oder sogar schädliche Ratschläge gibt [6].</p>

<p>Zusätzlich tragen mehrdeutige oder inkonsistente Eingabeaufforderungen maßgeblich zu Halluzinationen bei. Wenn Nutzer unklare oder widersprüchliche Anweisungen geben, interpretieren KI-Systeme diese Informationen oft falsch, was zu grotesken oder ungenauen Ausgaben führen kann. Ein Beispiel hierfür ist ein KI-Assistent, der unterschiedliche Antworten auf eine simple Anfrage gibt, abhängig von der Formulierung der Frage. Beispiele von Fehlinterpretationen in solchen Szenarien zeigen, wie wichtig klare und präzise Eingaben sind, um die Integrität der Resultate zu gewährleisten [7].</p>

<p>Insgesamt ist es entscheidend, qualitativ hochwertige, vielfältige Daten zu nutzen, das Modell zu kalibrieren und präzise Anfragen zu stellen, um die negativen Auswirkungen von KI-Halluzinationen zu minimieren.</p>

<hr />

<h2>Retrieval-Augmented Generation als Lösungsansatz</h2>

<p>Retrieval-Augmented Generation (RAG) ist eine innovative Technologie, die darauf abzielt, die Präzision und Relevanz von Antworten in KI-Systemen zu erhöhen. RAG kombiniert generative Sprachmodelle mit einem Retrieval-Mechanismus, der es ermöglicht, spezifische Informationen aus externen Datenquellen zu extrahieren und in die Antwortgenerierung zu integrieren. Dies führt nicht nur zu einer verbesserten Genauigkeit, sondern auch zu fundierteren und kontextuell relevanten Ausgaben.</p>

<p>Ein zentraler Vorteil von RAG ist die signifikante Reduzierung von KI-Halluzinationen, indem KI-Antworten nicht länger isoliert durch das zugrunde liegende Modell generiert werden. Indem die Technologie die Antworten an reale Daten anknüpft, erhöhen sich die Vertrauenswürdigkeit und die Faktizität der Informationen. Dies ist besonders vorteilhaft für Unternehmen, die auf präzise und verlässliche Daten angewiesen sind, um fundierte Entscheidungen zu treffen [8].</p>

<p>Ein weiterer bemerkenswerter Vorteil von RAG liegt in der Möglichkeit, aktuelle Informationen zu nutzen. Traditionelle Modelle sind oft auf statische Datenbasen angewiesen und können veraltete oder irrelevante Antworten generieren. RAG hingegen ermöglicht den Zugriff auf die neuesten Informationen, was besonders wichtig ist in schnelllebigen Branchen, in denen Informationen kontinuierlich aktualisiert werden müssen [9].</p>

<p>Die Kosten- und Ressourceneffizienz ist ein weiterer Vorteil, den RAG Unternehmen bietet. Durch die Integration externer Datenreduzierungen entfällt der häufig nötige umfangreiche und teure Prozess der Modell-Neutrainierung. Dies ermöglicht eine schnellere Implementierung von KI-Lösungen und senkt die Betriebskosten [10].</p>

<p>Zusätzlich fördert RAG ein besseres Verständnis des Kontexts, da relevante externe Informationsquellen einbezogen werden. Dies führt zu maßgeschneiderten und kontextsensitiven Antworten, die die Nutzererfahrung erheblich verbessern können [11].</p>

<p>Durch die Stärkung des Wissensmanagements und die Schaffung einer nachvollziehbaren Dokumentationsbasis trägt RAG ebenfalls zur Einhaltung von Compliance-Vorgaben bei, was für viele Organisationen von entscheidender Bedeutung ist [12].</p>

<hr />

<h2>Technologische Entwicklungen und Cloud-Plattformen</h2>

<p>Die jüngsten technologischen Fortschritte im Bereich der Retrieval-Augmented Generation (RAG) sind signifikant und entscheidend für die Weiterentwicklung von KI-Anwendungen. Cloud-Plattformen spielen eine zentrale Rolle bei der Bereitstellung der notwendigen Infrastruktur und Flexibilität, um diese Technologien zu implementieren und zu skalieren. Große Anbieter wie Google Cloud, AWS und Oracle integrieren zunehmend RAG-Funktionen in ihre Dienstleistungen. Zum Beispiel unterstützen Dienste wie die Google Cloud AI Platform, BigQuery und Vertex AI die Implementierung von RAG-Systemen, während Oracle Integration als Datenorchestrator in Multi-Cloud-RAG-Architekturen fungiert, was eine nahtlose Nutzung in verschiedenen Umgebungen ermöglicht [14].</p>

<p>Ein bemerkenswerter Trend ist die Zunahme von Open-Source-Projekten, die die Implementierung von RAG erleichtern. Projekte wie LightRAG, Haystack und RAG-Chain ermöglichen es Entwicklern, benutzerdefinierte RAG-basierte Systeme zu erstellen. Diese Frameworks tragen zur Verbreitung von RAG bei und senken die Barrieren für den Zugang zu leistungsfähigen KI-Lösungen [15].</p>

<p>Die Kernmechanismen von RAG-Systemen werden ebenfalls ständig verbessert. Die Fähigkeit, Informationen dynamisch zu integrieren und kontextbezogene Antworten zu generieren, hat sich erheblich weiterentwickelt. Dies führt zu präziseren und relevanteren Ergebnissen, da Echtzeit-Datenabfragen und -integrationen nun Standard sind. Insbesondere im Kontext multimodaler KI werden zunehmend verschiedene Datentypen wie Bilder, Audio und Video integriert, um umfassendere und kontextuell reichhaltigere Antworten zu liefern [16].</p>

<p>Zudem erkennen Entwickler das Potenzial von RAG zur Verbesserung der Softwareentwicklung, etwa durch effizientere Codierung und Debugging. Dies hat nicht nur die Entwicklungszyklen beschleunigt, sondern auch die Qualität des Codes insgesamt erhöht. In einer Ära, in der schnelle und qualitativ hochwertige Softwarelösungen unumgänglich sind, zeigt sich RAG als Schlüsseltechnologie [17].</p>

<hr />

<h2>Zukunftsperspektiven für KI und RAG</h2>

<p>Die Zukunftsperspektiven für die Künstliche Intelligenz (KI) und die Retrieval-Augmented Generation (RAG) zeigen eine aufregende Entwicklung, die Unternehmen neue Möglichkeiten eröffnet, ihre Prozesse und Dienstleistungen zu optimieren. RAG, das als zentraler Ansatz zur Verbesserung der Qualität und Zuverlässigkeit generativer KI angesehen wird, ergänzt KI-Systeme mit aktuellen und domänenspezifischen Informationen. Dies ermöglicht präzisere und kontextualisierte Antworten auf Anfragen, was für Unternehmen von entscheidender Bedeutung ist [18].</p>

<p>Ein wichtiger Trend in diesem Bereich ist die Verbesserung der Genauigkeit und Aktualität von Antworten durch den Zugriff auf externe Wissensdatenbanken. RAG-Systeme eröffnen neue Möglichkeiten, maßgeschneiderte Informationen bis hin zu Echtzeitdaten abzurufen, was für eine Vielzahl von Anwendungen, insbesondere im Finanz-, Gesundheits- und Kundenservice-Sektor, unerlässlich ist [12]. Darüber hinaus wird RAG als kosteneffiziente Lösung angesehen, die es Unternehmen ermöglicht, Ressourcen zu sparen, indem das kontinuierliche Neutraining von Modellen minimiert wird [22].</p>

<p>Ein weiterer interessanter Aspekt ist die zunehmende Fähigkeit von RAG, multimodale Daten zu verarbeiten. Dies bedeutet, dass zukünftige Systeme in der Lage sein werden, Text, Bilder und Videos zu integrieren und somit umfassendere und umfangreichere Analysen zu ermöglichen [19] [20].</p>

<p>Für Unternehmen ist es entscheidend, sich auf diese bevorstehenden Veränderungen vorzubereiten. Dazu gehört das Investieren in Weiterbildung, um die Mitarbeitenden auf die Nutzung der neuen Technologien vorzubereiten und eine agile Anpassung der Geschäftsprozesse zu ermöglichen. Die Bereitschaft zur Implementierung neuer Tools und die kontinuierliche Evaluierung der eigenen Datenstrategien sind ebenfalls wesentliche Faktoren, um im sich schnell wandelnden Umfeld wettbewerbsfähig zu bleiben.</p>

<hr />

<h2>Schlussfolgerungen</h2>

<p>Zusammenfassend lässt sich sagen, dass die Herausforderungen durch KI-Halluzinationen weiterhin bestehen und bedeutende Forschungen erfordern. Die Retrieval-Augmented Generation bietet jedoch vielversprechende Lösungen, um diese Halluzinationen zu reduzieren und die Effizienz von KI-Modellen zu steigern. Der fortlaufende technologische Fortschritt wird die Integration von RAG in verschiedenen Anwendungen vorantreiben und die Entwicklung zuverlässiger KI-Systeme fördern. Zukünftige Studien sollten sich darauf konzentrieren, Rahmenbedingungen zu schaffen, die sowohl die Datenqualität verbessern als auch innovative Ansätze der KI-Entwicklung unterstützen.</p>

<hr />

<h2>Quellen</h2>

<ol>
<li><a href="https://en.wikipedia.org/wiki/Artificial_intelligence">wikipedia.org</a>  </li>
<li><a href="https://www.nngroup.com/articles/ai-hallucinations/">nngroup.com</a>  </li>
<li><a href="https://bitpanda.com/academy/en/lessons/ai-hallucinations">bitpanda.com</a>  </li>
<li><a href="https://www.datacamp.com/blog/ai-hallucination">datacamp.com</a>  </li>
<li><a href="https://www.datacamp.com/de/blog/ai-hallucination">datacamp.com</a>  </li>
<li><a href="https://www.ki-expertenforum.de/ki-news/">ki-expertenforum.de</a>  </li>
<li><a href="https://geekflare.com/de/ai-hallucination/">geekflare.com</a>  </li>
<li><a href="https://www.signitysolutions.com/blog/trends-in-active-retrieval-augmented-generation">signitysolutions.com</a>  </li>
<li><a href="https://iotbusinessnews.com/2025/02/11/31300-retrieval-augmented-generation-the-secret-to-building-smarter-more-adaptive-ai-systems/">iotbusinessnews.com</a>  </li>
<li><a href="https://www.stxnext.com/blog/retrieval-augmented-generation-giving-ai-context-for-tailored-results/">stxnext.com</a>  </li>
<li><a href="https://www.microsoft.com/en-us/microsoft-cloud/blog/2025/02/13/5-key-features-and-benefits-of-retrieval-augmented-generation-rag/">microsoft.com</a>  </li>
<li><a href="https://www.wissen.com/blog/lets-discuss-the-rise-of-retrieval-augmented-generation-rag">wissen.com</a>  </li>
<li><a href="https://www.matillion.com/learn/blog/rag-retrieval-augmented-generation">matillion.com</a>  </li>
<li><a href="https://www.gorrion.io/blog/retrieval-augmented-generation-rag-ai/">gorrion.io</a>  </li>
<li><a href="https://www.restack.io/p/retrieval-augmented-generation-answer-github-projects-cat-ai">restack.io</a>  </li>
<li><a href="https://blogs.nvidia.com/blog/what-is-retrieval-augmented-generation/">nvidia.com</a>  </li>
<li><a href="https://www.k21academy.com/ai-ml/an-overview-of-retrieval-augmented-generationrag-and-ragops/">k21academy.com</a>  </li>
<li><a href="https://graphapi.com/de/learn/what-is-rag-genai/">graphapi.com</a>  </li>
<li><a href="https://www.ayadata.ai/the-state-of-retrieval-augmented-generation-rag-in-2025-and-beyond/">ayadata.ai</a>  </li>
<li><a href="https://www.embedded-software-engineering.de/mit-graphdatenbanken-die-ki-optimieren-a-253832231730698956490abb23091506/">embedded-software-engineering.de</a>  </li>
<li><a href="https://banking.vision/graphrag-im-fokus/">banking.vision</a>  </li>
<li><a href="https://www.restack.io/p/ai-development-trends-answer-rag-industry">restack.io</a></li>
</ol>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Automation mit n8n - Telegram zu E-Mail]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/automatisierung-mittels-n8n-telegram-zu-e-mail" />
      <id>tag:https:2025:/next-direction.de/2.26</id>
      <published>2025-02-06T16:00:00Z</published>
      <updated>2025-02-15T19:35:06Z</updated>
      <author>
            <name>ndn8n-r</name>
            <email>info@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/Automation-n8n.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/Automation-n8n.jpg" alt="Automation mittels n8n"/><br>
        <p>Willkommen zurück! Die manuelle Recherche und das Verfassen von Inhalten können oft zeitaufwendig und mühsam sein. Besonders bei komplexen Themen, die eine tiefgehende Recherche erfordern, kann der Prozess schnell überwältigend werden. Doch was, wenn ein Großteil dieser Arbeit automatisiert werden könnte? In diesem Artikel zeige ich, wie ihr mithilfe von n8n und mehrstufigen KI-Agenten einen vollständigen Workflow zur Inhaltserstellung aufbaut – von der ersten Idee, die per Telegram-Nachricht eingeht, bis hin zum fertigen Artikel, der per E-Mail verschickt wird.</p>

<h2>Überblick des automatisierten Workflows</h2>

<p>Der Workflow beginnt mit einer einfachen Telegram-Nachricht, in der das Thema des gewünschten Artikels angegeben wird. Diese Nachricht löst eine Reihe von Schritten aus, die von verschiedenen KI-Agenten übernommen werden. Zuerst wird eine Grundstruktur des Artikels erstellt, dann folgt die detaillierte Planung der einzelnen Abschnitte. Anschließend übernimmt ein Recherche-Agent die Aufgabe, relevante Informationen zu sammeln, bevor ein Editor-Agent den finalen Text verfasst. Das Ergebnis wird schließlich per E-Mail zugestellt.</p>

<h2>Schritt-für-Schritt-Durchführung</h2>

<p>Im Folgenden findet ihr nun die einzelnen Schritte etwas detaillierter erklärt. Wenn man so genauer darüber nachdenkt ist es schon sehr beeindruckend, was mit den verwendeten Werkzeugen mittlerweile alles erreicht werden kann.</p>

<h3>Schritt 1: Auslösen des Workflows per Telegram</h3>

<p>Der gesamte Prozess beginnt mit einer einfachen Nachricht in Telegram. Ein eingerichteter Telegram-Bot, der mit n8n verbunden ist, empfängt eure Nachricht. Diese Nachricht enthält das gewünschte Thema des Artikels, zum Beispiel: „Neueste Entwicklungen im Bereich erneuerbarer Energien.“ Sobald die Nachricht eingeht, wird sie von n8n erfasst und an die nächste Stufe des Workflows weitergeleitet. Dieser Schritt ist der Ausgangspunkt für die gesamte Automatisierung und stellt sicher, dass ich den Prozess bequem und ohne technische Hürden starten kann. Nach jedem der hier genannten Schritte bekomme ich den Zwischenstand erneut über Telegram geschickt.</p>

<h3>Schritt 2: Erstellung der Artikelstruktur</h3>

<p>Nachdem das Thema erfasst wurde, übernimmt der erste KI-Agent, der sogenannte Outline-Agent, die Aufgabe, eine grundlegende Struktur für den Artikel zu erarbeiten. Er kann dazu auf Internet-Recherchen mittels PerplexityAI zugreifen. Dieser Agent nutzt außerdem ChatGPT 4o mini von OpenAI, um basierend auf dem eingegebenen Thema eine Gliederung zu generieren. Die Gliederung umfasst typischerweise eine Einleitung, mehrere Hauptabschnitte und ein Fazit. Zum Beispiel könnte die Struktur für das Thema „erneuerbare Energien“ so aussehen:</p>

<ul>
<li>Einleitung – Bedeutung erneuerbarer Energien</li>
<li>Abschnitt 1 – Solarenergie</li>
<li>Abschnitt 2 – Windenergie</li>
<li>Abschnitt 3 – Herausforderungen und Lösungen</li>
<li>Fazit – Zukunftsperspektiven</li>
</ul>

<p>Diese Struktur dient als Gerüst für den weiteren Workflow und stellt sicher, dass der Artikel logisch und übersichtlich aufgebaut ist.</p>

<h3>Schritt 3: Detaillierte Planung der Abschnitte</h3>

<p>Im nächsten Schritt wird die grobe Struktur in eine detaillierte Planung umgewandelt. Hier kommt der Planungs-Agent ins Spiel. Dieser Agent nimmt die Gliederung des Outline-Agenten und zerlegt sie in kleinere, konkrete Aufgaben. Zum Beispiel könnte er für den Abschnitt über Solarenergie festlegen, dass dieser eine Definition von Solarenergie, aktuelle Technologietrends und ein Fallbeispiel enthalten soll. Diese Planung wird in einem strukturierten Format wie JSON weitergegeben, sodass sie leicht von den nachfolgenden Agenten verarbeitet werden kann. Der Planungs-Agent sorgt dafür, dass jeder Abschnitt des Artikels klar definiert ist und alle notwendigen Informationen abgedeckt werden. Das Ergebnis dieses Agenten ist dabei eine Liste abzuarbeitender Recherchen. Für jeden einzelnen Punkt wird anschließend Schritt 4 durchlaufen.</p>

<h3>Schritt 4: Tiefgehende Recherche mit PerplexityAI</h3>

<p>Sobald die detaillierte Planung vorliegt, beginnt die eigentliche Recherche. Hier kommt der Recherche-Agent zum Einsatz, der auf PerplexityAI basiert. Dieser Agent durchsucht das Internet nach relevanten Informationen für jeden geplanten Abschnitt. Zum Beispiel könnte er für den Abschnitt über Windenergie nach aktuellen Studien, Statistiken zur Effizienz von Windkraftanlagen und Zitaten von Experten suchen. Die gesammelten Daten werden dann in einem strukturierten Format gespeichert und an den nächsten Agenten weitergeleitet. Dieser Schritt ist besonders wichtig, da er sicherstellt, dass der Artikel auf fundierten und aktuellen Informationen basiert. PerplexityAI hat dabei einen weiteren wichtigen Vorteil. Wer sich schon Mal mit dem Thema Halluzinationen von künstlicher Intelligenz befasst hat, weiß, wie wichtig es ist, bestimmte Aussagen auch verifizieren zu können. PerplexityAI liefert deshalb zu jedem gefundenen Punkt auch die Quelle in Form von Links zu der jeweiligen Webseite. Im finalen Stand des Artikels wird dazu am Ende auch eine komplette Liste aller verwendeten Quellen erstellt.</p>

<h3>Schritt 5: Verfassen des Artikels durch den Editor-Agenten</h3>

<p>Nachdem alle notwendigen Informationen gesammelt wurden, übernimmt der Editor-Agent die Aufgabe, den Artikel zu verfassen. Dieser Agent kombiniert die gesammelten Daten zu einem flüssigen und gut strukturierten Text. Er verwendet dazu wiederum das ChatGPT 4o mini Modell von OpenAI. Er achtet dabei darauf, dass der Artikel einen klaren roten Faden hat, die richtige Tonlage trifft und SEO-optimiert ist. Zum Beispiel könnte er die Informationen über Solarenergie und Windenergie in einem zusammenhängenden Text darstellen, der sowohl informativ als auch ansprechend geschrieben ist. Der fertige Entwurf wird dann an den letzten Schritt des Workflows weitergegeben.</p>

<h3>Schritt 6: Versand des Artikels per E-Mail</h3>

<p>Zum Abschluss wird der fertige Artikel per E-Mail an den Nutzer gesendet. Hier kommt n8n erneut ins Spiel, um den E-Mail-Versand zu konfigurieren. Der Artikel kann entweder im Textformat oder als HTML-Nachricht verschickt werden. Dieser Schritt stellt sicher, dass ich den fertigen Artikel bequem und direkt in mein Postfach erhalte. Das ist durchaus nötig, da der fertige Artikel sehr lang werden kann, und es per Telegram nicht wirklich brauchbar wäre, weder fürs Lesen, noch für etwaige Weiterverarbeitung zu Blog-Artikeln oder um es an weitere Interessenten weiterzuleiten.</p>

<p>Durch diese Schritt-für-Schritt-Durchführung wird der gesamte Prozess der Recherche und Inhaltserstellung nahtlos automatisiert. Jeder Schritt baut auf dem vorherigen auf und sorgt dafür, dass der finale Artikel nicht nur schnell, sondern auch qualitativ hochwertig ist. In der Regel erhalte ich bereits nach 5 bis maximal 10 Minuten die fertige Recherche in mein Postfach.</p>

<h2>Technische Umsetzung</h2>

<p>Um diesen Workflow zu realisieren, sind einige technische Voraussetzungen notwendig. Zunächst benötigt ihr eine laufende Instanz von n8n, die entweder in der Cloud oder selbst gehostet betrieben werden kann. Darüber hinaus sind API-Schlüssel für die verwendeten Dienste wie OpenAI, PerplexityAI, Telegram und den E-Mail-Anbieter erforderlich. Die Konfiguration der einzelnen Schritte in n8n erfordert etwas Einarbeitung, ist aber dank der benutzerfreundlichen Oberfläche gut zu bewältigen.</p>

<h2>Herausforderungen und Lösungen</h2>

<p>Bei der Automatisierung solcher Workflows gibt es einige Herausforderungen. Zum Beispiel kann die Datenformatierung zwischen den einzelnen KI-Agenten Probleme bereiten. Hier ist es wichtig, klare Schnittstellen zu definieren und die Ausgaben der Agenten in einem einheitlichen Format wie JSON zu halten. Auch API-Limits, insbesondere bei der Nutzung von PerplexityAI, können den Prozess verlangsamen. Hier helfen Rate-Limits und eine sorgfältige Planung der Anfragen. Schließlich ist die Qualität der KI-Ausgaben stark von der Formulierung der Prompts abhängig. Eine gute Prompt-Engineering-Strategie ist daher unerlässlich. An der Stelle sei gesagt, dass ich nicht den kompletten Ablauf selbst erfunden habe. Ich habe dabei auf eine Vorlage durch einen Youtuber zurückgegriffen, der auch einen entsprechenden Udemy Kurs veröffentlich hat. Ich darf euch daher den vollständigen Ablauf auch nicht zur Verfügung stellen. Hier zur Orientierung trotzdem das Ergebnis in n8n:</p>

<p><img src="/images/uploads/n8n-research-workflow.jpg" alt="n8n Recherche-Automatisierung" /></p>

<h2>Vorteile der Automatisierung</h2>

<p>Die Automatisierung des Recherche- und Schreibprozesses bietet zahlreiche Vorteile. Der offensichtlichste ist die Zeitersparnis. Was früher Stunden oder sogar Tage gedauert hat, kann nun in wenigen Minuten erledigt werden. Darüber hinaus sorgt die Automatisierung für eine höhere Konsistenz in der Qualität der Inhalte. Und schließlich ist der Workflow skalierbar, sodass er problemlos auf mehrere Anfragen gleichzeitig angewendet werden kann.</p>

<h2>Kosten</h2>

<p>Vielleicht fragt sich jetzt der Ein oder Andere, was kostet mich der ganze Spaß? Da kann ich euch beruhigen, eine einzige Recherche kostet ca. 0,10 € bis 0,20 €. Und das obwohl ich für die Recherche mittlerweile auf das Sonar Pro Modell von PerplexityAI setze. Es liefert einfach bessere Ergebnisse und ist die paar Cent mehr auf jeden Fall wert.</p>

<p>Man kann sowohl bei PerplexityAI als auch bei OpenAI einstellen, dass bei Unterschreitung eines gewissen Betrags automatisch wieder um 5 € aufgefüllt wird. Ich habe mittlerweile an die 30 Themen recherchieren lassen und bisher hat PerplexityAI ein einziges Mal aufgefüllt, bei OpenAI bin ich erst bei etwas über einem Euro gelandet.</p>

<p>Wichtig war mir, dass ich nichts bezahle, wenn ich nichts nutze. Alles was ich hier vorgestellt habe, kommt ohne monatliche Fixkosten aus. Ihr zahlt lediglich die tatsächlich benötigten API-Aufrufe des jeweiligen Anbieters.</p>

<p>Da sich einige Anbieter auf dem Gebiet im Wettstreit befinden, werden die Kosten künftig sicherlich noch weiter Fallen. Außerdem wird die Qualität sicherlich zunehmen mit jeder Generation der verwendeten Modelle.</p>

<h2>Mögliche Erweiterungen</h2>

<p>Der beschriebene Workflow ist bereits sehr leistungsfähig, aber es gibt noch viele Möglichkeiten zur Erweiterung. Zum Beispiel könnte man eine menschliche Überprüfung in den Prozess integrieren, indem der fertige Artikel zunächst an einen Slack-Kanal gesendet wird, bevor er per E-Mail verschickt wird. Auch die Unterstützung mehrerer Sprachen wäre eine interessante Erweiterung, sodass der Artikel am Ende in mehrere Sprachen übersetzt wird, bevor er per Mail verschickt wird.</p>

<h2>Fazit</h2>

<p>Die Automatisierung von Recherche und Inhaltserstellung durch n8n und KI-Agenten ist ein mächtiges Werkzeug, das nicht nur Zeit spart, sondern auch die Qualität und Konsistenz der Inhalte verbessert. Mit den richtigen Tools und einer gut durchdachten Workflow-Struktur lässt sich der gesamte Prozess von der Idee bis zum fertigen Artikel nahtlos automatisieren. Probiert es am besten selber aus, wenn euch das Thema interessiert.</p>

<p>Für alle, die diesen Workflow selbst ausprobieren möchten, findet ihr hier einige nützliche Links:</p>

<ul>
<li><a href="https://docs.n8n.io/">n8n Dokumentation</a></li>
<li><a href="https://core.telegram.org/bots">Telegram Bot API</a></li>
<li><a href="https://docs.perplexity.ai/home">PerplexityAI API</a></li>
<li><a href="https://platform.openai.com/docs/overview">OpenAI API</a></li>
<li><a href="https://www.udemy.com/course/introduction-to-ai-automation-with-n8n-and-langchain">Udemy Kurs n8n inkl. vorgestelltem Workflow</a></li>
</ul>

<p>Nun will ich euch aber nicht mehr länger aufhalten. Wir lesen uns das nächste Mal auf Next Direction!</p>

<p>P.S. bei der Erstellung dieses Artikels hat mich übrigens DeepSeek V3 unterstützt. Das spart sehr viel Zeit bei der Ausformulierung und so kann ich euch weiterhin häufig neue Artikel zur Verfügung stellen.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Bilder mittels ComfyUI und Flux.1-dev]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/comfyui-und-flux.1-dev" />
      <id>tag:https:2025:/next-direction.de/2.25</id>
      <published>2025-02-02T13:00:00Z</published>
      <updated>2025-02-02T09:22:54Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/AI-drawing.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/AI-drawing.jpg" alt="Roboter zeichnet auf Leinwand"/><br>
        <p>Willkommen zurück! Heute könnt ihr den ersten vollständig von KI recherchierten und verfassten Beitrag lesen. Damit ihr einen Vergleich habt, dachte ich, als Thema in etwa das vom letzten Beitrag aufzugreifen.</p>

<p>Ich will auch kein Geheimnis daraus machen, dass ich manche Artikel mittels künstlicher Intelligenz erstellen lasse. Wie angedeutet habe ich einfach nicht mehr ganz so viel Zeit wie noch vor ein paar Jahren. Auch gesundheitlich bin ich nicht so fit wie früher und muss meine Bildschirmzeiten außerhalb der Arbeit etwas einschränken. Trotzdem will ich euch aber viele interessante Themen rund um das tägliche Leben in Verbindung mit Technik nicht vorenthalten.</p>

<p>Jeder Artikel, den ich hier veröffentliche, wird trotzdem von mir abschließend kontrolliert und gegengelesen.</p>

<p>Nun wünsche ich auch viel Spaß mit dem ersten Beitrag unseres &#8220;Gastautors&#8221;.</p>

<hr>

<h1>Künstliche Intelligenz in der Bildgenerierung: ComfyUI und Flux.1-dev</h1>

<p>In einer Welt, in der künstliche Intelligenz zunehmend die Art und Weise revolutioniert, wie wir Inhalte erstellen, zeigt die Kombination von ComfyUI und dem Flux.1-dev Modell neue Möglichkeiten zur Generierung von Bildern. Diese Technologien ermöglichen es Nutzern, durch einfache textbasierte Eingaben ästhetisch beeindruckende und detaillierte Bilder zu erzeugen. ComfyUI bietet eine modulare Benutzeroberfläche, während Flux.1-dev für seine außergewöhnlichen Ergebnisse bekannt ist, insbesondere bei komplexen Darstellungen. Der vorliegende Artikel untersucht das Potenzial und die Herausforderungen dieser innovativen Werkzeuge und deren Einfluss auf kreative Prozesse.</p>

<h2>Einführung in die Bildgenerierung mit KI</h2>

<p>AI-Bildgenerierung bezieht sich auf die Verwendung von künstlicher Intelligenz zur Erstellung visueller Inhalte. Hauptsächlich beruht dieser Prozess auf Deep Learning Modellen, die mit großen Datensätzen von Bildern und ihren entsprechenden Textbeschreibungen trainiert werden. In der aktuellen technologischen Landschaft haben sich Plattformen wie ComfyUI und das Flux.1-dev-Modell als zentrale Werkzeuge für die Bildgenerierung etabliert.</p>

<p>ComfyUI ist eine quelloffene Plattform für das Diffusionsmodell, die es Nutzern ermöglicht, fortschrittliche Bildgenerierungs-Pipelines zu entwerfen und auszuführen. Durch eine grafische Benutzeroberfläche können Benutzer verschiedene Schritte in einem Workflow definieren und visualisieren. Um Bilder zu generieren, nutzt ComfyUI spezifische Modelle wie das Flux.1-Modell, das für seine Effizienz und Anpassungsfähigkeit bekannt ist. Für die Nutzung des Flux.1-Modells müssen die notwendigen Modelle wie das Flux-Checkpoint-Modell und das CLIP-Visionsmodell heruntergeladen und im richtigen Verzeichnis innerhalb von ComfyUI gespeichert werden. Nach der Aktualisierung von ComfyUI und dem Laden eines Workflow-JSONs kann der Generierungsprozess initiiert werden, der durch einen Diffusionsprozess charakterisiert ist. <sup><a href="https://www.elegantthemes.com/blog/design/what-is-ai-art">1</a></sup></p>

<p>Der Diffusionsprozess spielt eine entscheidende Rolle in der KI-Bildgenerierung. Er beginnt oft mit einem zufälligen Bild oder Rauschen, welches durch wiederholtes Verfeinern zunehmend an Klarheit gewinnt. Dieser iterative Prozess orientiert sich an den Vorgaben des Textprompts und zielt darauf ab, das Bild so zu transformieren, dass es der gewünschten Ausgabe entspricht. Weitere wichtige Faktoren in diesem Kontext sind Textprompts und Seed-Werte. Textprompts leiten den Generierungsprozess, wobei präzisere Beschreibungen zu detaillierteren Bildern führen. Seed-Werte hingegen beeinflussen die Zufälligkeit – ein Seed-Wert von -1 sorgt für Zufälligkeit, während spezifische Werte die Reproduzierbarkeit gewährleisten. <sup><a href="https://modal.com/docs/examples/comfyapp">2</a></sup></p>

<p>Eine weitere bemerkenswerte Funktion von ComfyUI ist die Möglichkeit zur Anpassung durch benutzerdefinierte Knoten und parallelisierte Verarbeitung. Dies ermöglicht es Benutzern, mehrere Inputs gleichzeitig zu verarbeiten und verschiedene Aspekte der Bildgenerierung präzise zu steuern, etwa die Stärke der Bildanpassung. <sup><a href="https://towardsdatascience.com/learn-to-build-advanced-ai-image-applications-9c98d0f1f930">3</a></sup> Diese Anpassungsfähigkeit befähigt Nutzer, spezifische Anforderungen zu erfüllen und kreative Prozesse effizient zu optimieren.</p>

<h2>Die modulare Architektur von ComfyUI</h2>

<p>ComfyUI zeichnet sich durch seine modulare Architektur aus, die eine flexible und anpassungsfähige Benutzererfahrung bietet. Diese Struktur basiert auf einem node-basierten Workflow-System, das speziell für die Stable Diffusion KI entwickelt wurde. Dadurch können Benutzer ihre eigenen Workflows mit verschiedenen benutzerdefinierten Nodes erstellen, die die Funktionalität von ComfyUI erweitern. Die Installation und Verwaltung dieser Nodes erfolgt über den ComfyUI-Manager, was die Anpassung und Flexibilität des Systems erheblich erhöht <sup><a href="https://weirdwonderfulai.art/comfyui/getting-started-with-comfyui-in-2025/">4</a></sup>.</p>

<p>Die Benutzeroberfläche von ComfyUI ist darauf ausgelegt, die Benutzerfreundlichkeit zu maximieren. Die oberste Navigationsleiste ermöglicht es Nutzern, Workflows zu speichern, zu öffnen oder zu bearbeiten. Zudem werden Tabs bereitgestellt, um zwischen verschiedenen Workflows zu wechseln. Eine spezielle Leiste im unteren Bereich zeigt Live-Protokolle an, welche den Benutzern helfen, die Abläufe während der Workflow-Ausführung zu verfolgen und eventuelle Fehler zu identifizieren <sup><a href="https://stable-diffusion-art.com/how-to-install-comfyui/">5</a></sup>.</p>

<p>Ein herausragendes Merkmal ist die Möglichkeit, mehrere CFG-Skalen in einem einzigen Workflow zu verwenden, wodurch Benutzer die Ergebnisse vergleichen und optimieren können. Diese Funktion ist besonders nützlich beim Prototyping, da sie Entwicklern und Künstlern ermöglicht, verschiedene Ansätze zu testen und die besten Ergebnisse vor der finalen Produktion auszuwählen <sup><a href="https://aws.amazon.com/blogs/architecture/">6</a></sup>.</p>

<p>Die modulare Architektur von ComfyUI fördert nicht nur die Anpassung, sondern optimiert auch den Generierungsprozess. Die Lösung ist bekannt für ihre geringe Speicherauslastung und die schnelle Bildgenerierung, da sie nur die notwendigen Komponenten lädt. Dies führt zu einer effizienteren Ressourcennutzung und resultiert in einem insgesamt leistungsstarken System. Entwickler und Künstler profitieren enorm von dieser Flexibilität, da sie komplexe Workflows erstellen und deren Effizienz leicht überprüfen können <sup><a href="https://www.udemy.com/course/stable-diffusion-comfyui-vom-anfanger-zum-profi/">7</a></sup>.</p>

<p>Die modulare Struktur von ComfyUI macht es zur idealen Wahl für die KI-gestützte Bildgenerierung, da sie nicht nur eine Anpassung der Workflows erlaubt, sondern auch eine intuitive und effiziente Benutzererfahrung bietet.</p>

<h2>Qualität und Detailtreue mit Flux.1-dev</h2>

<p>Das Flux.1-dev-Modell ist eine bahnbrechende Entwicklung von Black Forest Labs, die speziell für Forschungs- und Entwicklungszwecke konzipiert wurde. Dieses Modell bietet eine herausragende Bildqualität, die mit der des Pro-Modells vergleichbar ist, wodurch es ideal für akademische Projekte und Tests im Bereich der KI-gestützten Bildgenerierung ist. Es ermöglicht die Erstellung qualitativ hochwertiger Bilder aus komplexen Inhalten, was in vielen Forschungsanwendungen von entscheidender Bedeutung ist <sup><a href="https://www.eachlabs.ai/blog/discover-the-power-of-flux-models-tools-pro-dev-and-schnell-explained">8</a></sup>.</p>

<p>Eine der bemerkenswertesten Eigenschaften des Flux.1-dev-Modells ist seine hybride Architektur, die multimodale und parallele Diffusions-Transformer-Blöcke kombiniert. Mit 12 Milliarden Parametern ermöglicht diese leistungsstarke Architektur die Generierung detaillierter und ansprechender Bilder, die auf Texteingaben reagieren. Dies macht das Modell extrem vielseitig und fügt der Bildgenerierung eine neue Dimension hinzu, insbesondere bei der Verarbeitung komplexer Inhalte, die häufig in akademischen oder künstlerischen Anwendungen vorkommen <sup><a href="https://9elements.com/blog/fine-tuning-flux-1-dev-model/">9</a></sup>.</p>

<p>Ein weiteres zentrales Merkmal des Flux.1-dev-Modells ist die Möglichkeit des Feintunings mit einer relativ geringen Anzahl von Bildern. Diese benutzerdefinierte Anpassung ermöglicht es den Forschern, das Modell gezielt für spezifische Aufgaben oder zur Erzeugung von Bildern entsprechend bestimmter Stile oder Themen zu optimieren. Diese Flexibilität ist insbesondere in der Forschung von Vorteil, da sie die Genauigkeit und Relevanz der erzeugten Bilder erhöht, wodurch das Modell für diverse Anwendungen nahtlos einsetzbar ist <sup><a href="https://www.koyeb.com/deploy/flux-dev">10</a></sup>.</p>

<p>Obwohl das Flux.1-dev-Modell nicht die Geschwindigkeit priorisiert, wie das Flux.1-schnell-Modell, bietet es dennoch eine effiziente Leistung, insbesondere bei der Bereitstellung auf leistungsstarker Cloud-Infrastruktur. Dies ermöglicht eine schnelle und qualitativ hochwertige Bildgenerierung, die auf die Bedürfnisse von Entwicklern und Forschern zugeschnitten ist <sup><a href="https://www.aimodels.fyi/models/replicate/flux-schnell-black-forest-labs">11</a></sup>.</p>

<p>Zusammenfassend lässt sich sagen, dass das Flux.1-dev-Modell ein äußerst leistungsfähiges Werkzeug für AI-Forscher und Entwickler darstellt, das qualitativ hochwertige Bildgenerierungsfähigkeiten ohne die Notwendigkeit einer kommerziellen Lizenz bietet.</p>

<h2>Praktische Anwendungsbeispiele von ComfyUI und Flux</h2>

<p>Die modulare und flexible Architektur von ComfyUI und die Integration von Flux.1 bieten kreativen Profis eine beeindruckende Palette an Möglichkeiten zur Bildgenerierung. Ein herausragendes Beispiel für die erfolgreiche Anwendung von ComfyUI in Verbindung mit Diffusions-Modellen ist die umfangreiche Nutzung durch TechnoLynx <sup><a href="https://www.technolynx.com/post/ai-art-generation-with-stable-diffusion">12</a></sup>. ComfyUI basiert auf einem node-basierten Workflow, der es Nutzern ermöglicht, den Bildgenerierungsprozess in verschiedene Schritte zu unterteilen. Diese Herangehensweise ist ideal für komplexe Werkabläufe, in denen Kunstwerke vom Skizzieren über die Generierung bis hin zur Animation geführt werden. Dabei können künstlerische Visionen präzise verwirklicht und bearbeitet werden, was den Kreativprozess erheblich optimiert.</p>

<p>Ein anschauliches Fallbeispiel ist die Anwendung von ComfyUI durch den Entwickler <strong>comfyanonymous</strong>, der Mitglied des Stability-Teams war und eine Schlüsselrolle bei der Entwicklung von ComfyUI spielte. Mit über 60.000 Stars auf GitHub ist ComfyUI eines der am schnellsten wachsenden Open-Source-Projekte im Bereich der generativen Bilder geworden. Diese Akzeptanz verdeutlicht, wie wichtig die Anpassbarkeit und Vielseitigkeit der Benutzeroberfläche für kreative Profis ist.</p>

<p>Flux, der leistungsstarke KI-Algorithmus, der mit ComfyUI kombiniert werden kann, setzt ebenfalls neue Standards in der Bildgeneration. Flux erlaubt eine optimale Feinabstimmung durch Techniken wie die Low-Rank-Adaption (LoRA) und die Verwendung von auf Attention basierenden Regularisierern, was besonders bei Projekten von Bedeutung ist, die eine präzise Kontrolle über den generierten Inhalt erfordern. In Kombination mit ComfyUI wird die Bildgenerierung nicht nur effizient, sondern auch hochqualitativ.</p>

<p>Gemeinsam bieten ComfyUI und Flux.1 wertvolle Instrumente, durch die kreative Profis nicht nur die Geschwindigkeit, sondern auch die Qualität ihrer Projekte steigern können. Ihre modulare Struktur und die Vielseitigkeit der verwendeten Tools revolutionieren die Art und Weise, wie Kunstwerke heute in digitalen Medien erschaffen werden.</p>

<h2>Zukunft der KI-basierten Bildgenerierung</h2>

<p>In der Zukunft der KI-basierten Bildgenerierung, besonders im Jahr 2025, zeichnen sich verschiedene Trends und Herausforderungen ab, die sowohl für Anwender als auch für Entwickler von Bedeutung sind. Einer der zentralen Trends ist die zunehmende Integration von Bildgenerierung und -bearbeitung. Programme wie Adobe Firefly und Open-Source-Ökosysteme wie ComfyUI streben an, eine nahtlose Benutzeroberfläche zu schaffen, die den gesamten kreativen Prozess von der ersten Idee bis zur finalen Bearbeitung abdeckt. Dies ermöglicht eine intuitive Handhabung und fördert kreativere Ergebnisse <sup><a href="https://www.exovia.de/journal/ki-bilder-erstellen-beste-ki-bildgeneratoren/">13</a></sup>.</p>

<p>Ein weiterer wichtiger Trend ist die Entwicklung smarter Schnittstellen und automatisierter Workflows, die Bildgeneratoren mit Anwendungen wie Photoshop oder GIMP verbinden. Diese Schnittstellen könnten den kreativen Prozess beschleunigen und bieten KI-basierte Vorschläge, um Designentscheidungen zu erleichtern. Solche Automatismen könnten insbesondere für Kreativprofis eine wertvolle Unterstützung darstellen, indem sie Routineaufgaben optimieren <sup><a href="https://www.kunstplaza.de/ki-kunst/beste-kostenlose-ai-kunstgeneratoren/">14</a></sup>.</p>

<p>Trotz dieser positiven Entwicklungen sehen sich Anwender und Entwickler jedoch zahlreichen Herausforderungen gegenüber. Die Kosten für die Implementierung von KI-Technologien, insbesondere für kleinere Studios oder Einzelpersonen, können beträchtlich sein. Oft erfordern leistungsfähige Systeme die Anschaffung hochleistungsfähiger Grafikkarten und den Zugang zu umfangreichen Trainingsdaten, was eine erhebliche finanzielle Hürde darstellt <sup><a href="https://www.ki-und-fotografie.de/artikel/3d-welten-mit-ki/">15</a></sup>.</p>

<p>Ein weiteres bedeutsames Anliegen ist die Qualität der verwendeten Daten. Hochwertige Trainingsdaten sind entscheidend für die Herstellung präziser und realistischer Modelle. Eine mangelnde Datenqualität kann zu unvorteilhaften Eigenschaften in den generierten Bildern führen, was die Zuverlässigkeit der KI einschränkt. Zudem muss noch die rechtliche Dimension berücksichtigt werden, da Urheberrechtsfragen und potenzielle Missbrauchsmöglichkeiten im Zusammenhang mit KI-generierten Inhalten immer relevanter werden <sup><a href="https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/KI/Generative_KI-Modelle.pdf?__blob=publicationFile&amp;v=7">16</a></sup>.</p>

<p>Für die Zukunft sollten Benutzer und Entwickler Strategien entwickeln, um diese Herausforderungen zu meistern. Es wird empfohlen, sich aktiv mit den verfügbaren Werkzeugen und deren Möglichkeiten auseinanderzusetzen und fortlaufend zu lernen, um die eigene Kreativität optimal nutzen zu können. Die beständige Weiterentwicklung der Technologie eröffnet zudem zahlreiche Chancen für neuartige Anwendungen und kreative Ansätze, was die Bedeutung des Dialogs zwischen Fachleuten und der Community unterstreicht.</p>

<h2>Fazit</h2>

<p>Die Generierung von Bildern mit ComfyUI und dem Flux.1-dev Modell stellt einen bedeutenden Fortschritt in der kreativen Nutzung von künstlicher Intelligenz dar. Durch die Kombination aus modularer Anpassungsfähigkeit und hoher Bildqualität wird es sowohl Anfängern als auch Profis ermöglicht, kreative Projekte effizient umzusetzen. Die Herausforderungen, die sich aus der Nutzung solcher Technologien ergeben, können durch Bildung und ethische Überlegungen angegangen werden. Zukünftige Entwicklungen werden wahrscheinlich neue Maßstäbe setzen und die Art und Weise, wie visuelle Kunst erschaffen und konsumiert wird, weiter transformieren.</p>

<hr />

<h3>Quellen</h3>

<ol>
<li><a href="https://www.elegantthemes.com/blog/design/what-is-ai-art">Elegant Themes - What is AI Art?</a></li>
<li><a href="https://modal.com/docs/examples/comfyapp">Modal - ComfyUI Example</a></li>
<li><a href="https://towardsdatascience.com/learn-to-build-advanced-ai-image-applications-9c98d0f1f930">Towards Data Science - Learn to Build Advanced AI Image Applications</a></li>
<li><a href="https://weirdwonderfulai.art/comfyui/getting-started-with-comfyui-in-2025/">Weird Wonderful AI - Getting Started with ComfyUI in 2025</a></li>
<li><a href="https://stable-diffusion-art.com/how-to-install-comfyui/">Stable Diffusion Art - How to Install ComfyUI</a></li>
<li><a href="https://aws.amazon.com/blogs/architecture/">AWS - Architecture Blog</a></li>
<li><a href="https://www.udemy.com/course/stable-diffusion-comfyui-vom-anfanger-zum-profi/">Udemy - Stable Diffusion ComfyUI: Vom Anfänger zum Profi</a></li>
<li><a href="https://www.eachlabs.ai/blog/discover-the-power-of-flux-models-tools-pro-dev-and-schnell-explained">Each Labs - Discover the Power of Flux Models</a></li>
<li><a href="https://9elements.com/blog/fine-tuning-flux-1-dev-model/">9 Elements - Fine Tuning the Flux.1 Dev Model</a></li>
<li><a href="https://www.koyeb.com/deploy/flux-dev">Koyeb - Deploying the Flux Dev Model</a></li>
<li><a href="https://www.aimodels.fyi/models/replicate/flux-schnell-black-forest-labs">AI Models - Replicate the Flux Models</a></li>
<li><a href="https://www.technolynx.com/post/ai-art-generation-with-stable-diffusion">TechnoLynx - AI Art Generation with Stable Diffusion</a></li>
<li><a href="https://exovia.de/journal/ki-bilder-erstellen-beste-ki-bildgeneratoren/">Exovia - KI-Bilder Erstellen: Beste KI-Bildgeneratoren</a></li>
<li><a href="https://www.kunstplaza.de/ki-kunst/beste-kostenlose-ai-kunstgeneratoren/">Kunstplaza - Beste kostenlose AI Kunstgeneratoren</a></li>
<li><a href="https://www.ki-und-fotografie.de/artikel/3d-welten-mit-ki/">KI und Fotografie - 3D Welten mit KI</a></li>
<li><a href="https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/KI/Generative_KI-Modelle.pdf?__blob=publicationFile&amp;v=7">BSI - Generative KI-Modelle</a></li>
</ol>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Passgenaue Bilder mittels KI]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/passgenaue-bilder-mittels-ki" />
      <id>tag:https:2025:/next-direction.de/2.24</id>
      <published>2025-01-29T17:00:00Z</published>
      <updated>2025-01-29T17:40:17Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/Images-AI.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/Images-AI.jpg" alt="Images-AI"/><br>
        <p>Willkommen zurück! Wie versprochen gibt es heute den ersten von hoffentlich vielen weiteren Beiträgen. Wie angekündigt, werde ich mich zunächst mal auf meine neuen Abläufe für die Erstellung von Beiträgen konzentrieren. Als erste möchte ich euch daher meinen Flow zur Erstellung passender Bilder vorstellen. Viel Spaß mit den folgenden Ausführungen.</p>

<h2>Einführung</h2>

<p>Die Erstellung maßgeschneiderter Bilder durch KI hat in den letzten Jahren einen enormen Sprung gemacht. Während frühere Tools wie Stable Diffusion bereits beeindruckend waren, setzen moderne Modelle wie FLUX.1-dev neue Maßstäbe in Sachen Detailtreue und Kreativität. Doch mit dieser Entwicklung steigen auch die technischen Anforderungen: Hochwertige Generierungen benötigen leistungsstarke Hardware, die für viele Privatanwender schlicht zu teuer ist.</p>

<p>Hier kommt RunPod.io ins Spiel – ein Cloud-Dienst, der Zugang zu Highend-Grafikkarten wie der NVIDIA A40 mit 48 GB VRAM ermöglicht, ohne dass man dafür Tausende Euro investieren muss. Kombiniert mit der flexiblen Oberfläche ComfyUI und dem KI-Modell FLUX.1-dev ergibt sich ein Workflow, der nicht nur schnell und kosteneffizient ist, sondern auch künstlerische Freiheit auf Profi-Niveau bietet. Nach Jahren der Nutzung von Stable Diffusion und Tools wie Automatic1111 hat mich diese neue Kombination aber noch mehr überzeugt – warum, erkläre ich euch im Detail.</p>

<h2>RunPod.io: Leistung aus der Cloud</h2>

<p>Die Herausforderung bei modernen KI-Modellen ist bekannt: Je komplexer die Prompts und je höher die Auflösung, desto mehr Grafikspeicher (VRAM) wird benötigt. Selbst eine RTX 3090 mit 24 GB VRAM stößt hier schnell an Grenzen, insbesondere wenn Batch-Generierungen oder aufwendige Nachbearbeitungsschritte ins Spiel kommen.</p>

<p>RunPod.io löst dieses Problem elegant, indem es GPU-Ressourcen nach Bedarf vermietet. Ein sog. Pod mit einer NVIDIA A40 (48 GB VRAM) kostet beispielsweise nur etwa 40 Cent pro Stunde – und das ohne langfristige Verträge. Praktisch bedeutet das: Ihr startet den Cloud-Server, wenn ihr ihn braucht, und stoppt ihn nach dem Rendern wieder. Die Kosten bleiben so überschaubar, selbst bei intensiven Projekten.</p>

<p>Mein persönlicher Favorit ist das vorkonfigurierte Image valyriantech/comfyui-with-flux, das ComfyUI und FLUX.1-dev bereits vorinstalliert enthält. Nach dem Hochfahren des Pods dauert es nur wenige Minuten, bis die Oberfläche über den Browser erreichbar ist. Die Einrichtung ist damit nahezu plug-and-play, was wertvolle Zeit spart. Ein weiterer Pluspunkt: RunPod.io bietet Spot-Pricing an, bei dem ihr bis zu 70% sparen könnt, wenn ihr flexibel seid und kurzfristige Unterbrechungen akzeptiert. Ich nutze das persönlich gar nicht, da wie erwähnt die Kosten eh meistens im Cent Bereich sind.</p>

<h2>ComfyUI: Flexibilität trifft Performance</h2>

<p>Wer bereits Erfahrung mit Tools wie Automatic1111 gesammelt hat, weiß, dass die Benutzeroberfläche zwar intuitiv ist, aber bei komplexen Workflows schnell an Grenzen stößt. ComfyUI setzt hier auf einen radikal anderen Ansatz: Statt vordefinierter Buttons und Slider arbeitet man mit einem knoten-basierten System, bei dem jeder Schritt der Bildgenerierung als eigener Baustein dargestellt wird. Hier ein kleiner Eindruck davon:</p>

<p><img src="/images/uploads/ComfyUI-Workflow.jpg" alt="ComfyUI Beispiel-Workflow" /></p>

<p>Das mag zunächst abschreckend wirken, entfaltet aber schnell seinen Charme. Beispielsweise lässt sich ein Workflow erstellen, bei dem zunächst ein grobes Bild generiert, dann per ControlNet die Pose optimiert und schließlich ein Gesichtskorrektur-Knoten angewendet wird – alles in einer einzigen Kette. Der Clou: Diese Abläufe lassen sich als JSON-Dateien speichern, wiederverwenden oder mit der Community teilen.</p>

<p>Ein weiterer Vorteil ist die Performance. Im Vergleich zu Automatic1111 ist ComfyUI deutlich ressourcenschonender, insbesondere wenn Plugins oder Custom Nodes im Spiel sind. Es wird für die Ausführung jedes Workflows nämlich nur das geladen, was benötigt wird, während Automatic1111 beim Start der Anwendung bereits alle Plugins in den Speicher lädt. Die Startzeit bleibt bei ComfyUI nahezu gleich, egal wieviele Custom Nodes ihr installiert habt. Auch die Aktualisierung aller Komponenten ist wesentlich weniger Anfällig für Probleme, da jeweils nur das geladen wird, was für den aktuellen Ablauf benötigt wird.</p>

<h2>FLUX.1-dev: Das Modell für Perfektionisten</h2>

<p>Nach langer Zeit mit Stable Diffusion und SDXL hat mich FLUX.1-dev überzeugt. Was es besonders macht, ist die Präzision. Prompts wie &#8220;ein Wikingerschiff im Sturm, mit detaillierten Holztexturen und Runen am Bug, im Stil von John Howe&#8221; werden nicht nur verstanden, sondern auch umgesetzt. Farben bleiben klar getrennt, und die Komposition orientiert sich eng an den Vorgaben.</p>

<p>Ein weiterer Durchbruch ist die Texteinbindung. Während viele Modelle scheitern, sobald Schriftzüge Teil des Bildes sein sollen, liefert FLUX.1-dev hier zuverlässige Ergebnisse. In einem Test mit dem Prompt &#8220;Streetwear-Logo auf einem grauen Hoodie, Schriftzug CYBERPUNK in Neon-Pink&#8221; war das Logo bereits nach wenigen Versuchen lesbar und stylisch integriert – ein Quantensprung gegenüber SDXL, wo solche Szenarien oft 20+ Iterationen benötigten, wenn sie überhaupt jemals funktioniert haben. Hier der Beweis, nach dem ersten Versuch:</p>

<p><img src="/images/uploads/ComfyUI-Image.jpg" alt="ComfyUI Beispiel-Bild" /></p>

<p>Generell lässt sich sagen, dass es bei einfacheren Texten meist sogar aufs erste Mal funktioniert, während z.B. kurze Sätze durchaus auch mal etwas mehr Versuche benötigen.</p>

<p>Auch die Anatomie überzeugt: Hände mit fünf Fingern, proportionale Gliedmaßen und natürliche Posen sind kein Zufallstreffer mehr, sondern Standard. Das mag trivial klingen, macht aber einen enormen Unterschied, wenn man professionelle Assets erstellen will, die nicht nach „KI-generiert“ aussehen.</p>

<h2>Fazit: Warum KI-Bilder meine Workflows revolutioniert haben</h2>

<p>Vor einigen Jahren noch habe ich Stunden damit verbracht, passende Stock-Fotos zu suchen oder lizenzfreie Bilder zu suchen. Heute generiere ich die meisten Grafiken selbst – schneller, günstiger und vor allem passgenauer. Die Kombination aus RunPod.io, ComfyUI und FLUX.1-dev hat dabei alles verändert.</p>

<p>Klar, der Einstieg erfordert technisches Know-how. Wer sich aber einmal mit ComfyUI’s Nodes vertraut gemacht hat, entdeckt ein Werkzeug, das kaum Wünsche offenlässt. Und dank RunPod.io muss man nicht einmal tief in die Tasche greifen, um die nötige Power zu nutzen.</p>

<p>Für mich ist klar: KI-Bildgenerierung ist kein Hype mehr, sondern ein fester Bestandteil meines Werkzeugkastens. Ob Social-Media-Inhalte, Concept Art oder Mockups – die Flexibilität, jedes Bild genau nach Vorgabe zu erstellen, ist unschlagbar. Wer bereit ist, sich in die Technik einzuarbeiten, wird belohnt – mit kreativer Freiheit, die früher unvorstellbar war.</p>

<p>Habt ihr eigene Erfahrungen mit Runpod, ComfyUI oder FLUX gemacht? Oder Fragen zu spezifischen Use-Cases? Diskutiert gerne mit auf Slack (Link im Footer) – ich bin gespannt auf eure Insights!</p>

<p>Nun möchte ich euch aber nicht länger aufhalten. Bis zum nächsten Mal auf Next Direction!</p>

<p>P.S. der nächste Beitrag wird sich mit dem gleichen Thema beschäftigen, wird größtenteils von KI generiert sein, aber für weniger technisch versierte Benutzer eher allgemein gehalten.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[In eigener Sache: Zukunft des Blogs]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/in-eigener-sache-blog-zukunft" />
      <id>tag:https:2025:/next-direction.de/2.23</id>
      <published>2025-01-26T13:56:00Z</published>
      <updated>2025-01-26T15:56:43Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/new_age.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/new_age.jpg" alt="New age"/><br>
        <p>Willkommen zurück! Ein neues Kapitel beginnt, und ihr könnt dabei sein.</p>

<p>Es ist eine Weile her – genauer gesagt, viel zu lange. Ich möchte mich zunächst bei euch bedanken, dass ihr noch hier seid, und gleichzeitig erklären, warum es so still geworden ist.</p>

<p>Die letzten Jahre waren für mich persönlich eine Herausforderung. Gesundheitliche Probleme haben mich dazu gezwungen, einen Gang zurückzuschalten und mich auf das Wesentliche zu konzentrieren. Das bedeutete leider auch, dass ich viele meiner privaten Projekte, über die ich hier regelmäßig berichtet habe, zurückfahren musste. Es war keine leichte Entscheidung, aber sie war notwendig, um wieder zu Kräften zu kommen.</p>

<h2>Was euch in Zukunft erwartet</h2>

<p>Jetzt, da ich mich besser fühle und wieder mehr Energie habe, ist es an der Zeit, diesen Blog neu zu beleben – und zwar mit einigen spannenden Veränderungen!</p>

<h3>Erweiterte Themenvielfalt</h3>

<p>Während es in der Vergangenheit vor allem um Web-Entwicklung und private Projekte ging, wird der Blog zukünftig ein breiteres Spektrum an technischen Themen abdecken. Von Cloud-Computing über DevOps bis hin zu neuen Programmiersprachen – ich möchte euch Einblicke in die vielfältige Welt der Technologie geben.</p>

<h3>Über den Tellerrand hinaus</h3>

<p>Neben technischen Inhalten wird es aber hin und wieder auch Beiträge zu anderen Themen geben, die mich beschäftigen. Das können Gedanken zu aktuellen gesellschaftlichen Entwicklungen, persönliche Erfahrungen oder sogar Empfehlungen aus den Bereichen Bücher, Filme oder Finanzen sein. Ich glaube, dass ein guter Mix aus Technik und Leben den Blog noch interessanter macht.</p>

<h3>Künstliche Intelligenz als zentrales Thema</h3>

<p>Ein Bereich, der mich besonders fasziniert und der in Zukunft eine große Rolle auf diesem Blog spielen wird, ist die Künstliche Intelligenz (KI). KI verändert nicht nur die Tech-Welt, sondern auch die Art und Weise, wie wir Inhalte erstellen und konsumieren.</p>

<h4>KI-generierte Bilder</h4>

<p>Ihr werdet feststellen, dass die Bilder in meinen Beiträgen besser zu den Inhalten passen. Das liegt daran, dass sie mithilfe von FLUX.1-dev erstellt werden – einem Tool, das auf KI basiert und erstaunlich präzise visuelle Inhalte generieren kann.</p>

<h4>KI-unterstützte Inhalte:</h4>

<p>Auch die Texte werden in Zukunft größtenteils von KI recherchiert und verfasst. Aber keine Sorge – die letzte Kontrolle liegt immer bei mir. Ich sehe die KI als Werkzeug, das mir hilft, schneller und effizienter zu arbeiten, ohne dabei die Qualität oder die persönliche Note zu verlieren.</p>

<h2>Warum dieser Wandel?</h2>

<p>Die Welt verändert sich rasant, und ich möchte mit diesem Blog nicht nur Schritt halten, sondern auch neue Wege gehen. Die Kombination aus technischer Expertise, persönlichen Einblicken und der Nutzung modernster Technologien wie KI soll euch einen Mehrwert bieten, der über das hinausgeht, was ich bisher angeboten habe.</p>

<p>Zum Abschluss möchte ich mich noch einmal bei euch bedanken – für eure Geduld, eure Treue und eure Unterstützung. Es bedeutet mir viel, dass ihr auch nach der langen Pause noch hier seid. Ich freue mich darauf, mit euch in dieses neue Kapitel zu starten und spannende Diskussionen zu führen.</p>

<p>Bleibt gespannt, denn es gibt viel zu erzählen! Kleiner Spoiler: beginnen werde ich vermutlich mit Themen die sich auf die neue Art der Beitragserstellung beziehen. Allen voran der Prozess der Bildgenerierung aber auch die Recherche-Arbeit durch ein sehr interessantes Open Source Projekt werden dabei im Fokus stehen.</p>

<p>Nun will ich euch aber nicht länger aufhalten. Bis bald auf Next Direction!</p>

<p>P.S.: Wie findet ihr die neuen KI-generierten Bilder und die Idee, KI in die Inhaltserstellung einzubinden? Ich bin gespannt auf eure Meinungen!</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Was ist: ein Design System?]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/was-ist-ein-design-system" />
      <id>tag:https:2020:/next-direction.de/2.22</id>
      <published>2020-12-06T15:00:00Z</published>
      <updated>2020-12-06T15:17:46Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/design-system.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/design-system.jpg" alt="Design System - Teaser"/><br>
        <p>Willkommen zurück! Heute möchte ich euch eine kleine Einführung zum Thema <strong>Design Systeme</strong> geben. Dabei soll es zunächst darum gehen, was ein Design System im weitesten Sinne ist, und was es beinhaltet. Danach folgt ein kleiner Ausflug in die Welt des atomaren Designs, bevor wir im letzten Teil des Beitrags dann ein Werkzeug verwenden werden, mit dem das eigene Design System dokumentiert werden kann. Wer mich kennt weiß, dass hier natürlich Open Source eine große Rolle spielt.</p>

<p>Es gibt wie immer eine Menge zu lesen, daher lasst uns direkt anfangen.</p>

<h2>Wozu ein Design System</h2>

<p>Ich muss gestehen, bis vor ungefähr einem halben Jahr, habe ich auch nicht gewusst, dass es so etwas wie Design Systeme in dieser Tiefe gibt. Seitdem ich es weiß, bin ich aber ein Fan davon. Auch beruflich werde ich mich die nächste Zeit sehr ausgiebig damit befassen. Ich war auch überrascht, wie viele Firmen, egal ob groß oder klein, ihre Design Systeme öffentlich zugänglich dokumentiert haben. Eines der bekannteren dürfte wohl das <a href="https://material.io/">Material Design</a> von Google sein. Aber auch <a href="https://www.microsoft.com/design/fluent/#/">Microsoft</a>, <a href="https://www.ibm.com/design/language/">IBM</a> oder <a href="https://atlassian.design/">Atlassian</a> haben ihre eigenen Systeme. Sehr oft beziehen sich solche Systeme auf webbasierte Software. Natürlich können die Systeme aber auch für Desktop Anwendungen oder Smartphone Apps herangezogen werden.</p>

<p>Es gibt viele Beweggründe, wieso eine Firma ein Design System erstellt. Allen voran bietet es eine gemeinsame Kommunikationsgrundlage wenn es um das Design der eigenen Anwendungen geht. Jeder kann sich die Dokumentation anschauen und sieht sofort, wie Anwendungen aufgebaut sein sollen, und welche Komponenten wie auszusehen haben. Designer können auf dieser Basis Mockups und Prototypen entwerfen, die von Entwicklern auf immer gleiche Weise umgesetzt werden können. So ergibt sich für Endbenutzer ein gleichbleibendes Erlebnis bei der Verwendung aller Anwendungen der jeweiligen Firma.</p>

<p>Wichtig ist, dass ein Design System dabei ein wachsender und sich weiterentwickelnder Organismus ist, genauso wie die Anwendungen selbst. Natürlich bedarf es zu Beginn einer gewissen Basis, die sich jedoch stetig verändert und auch mit der Zeit geht. Dabei sollten gewünschte Änderungen zuerst in das Design System integriert werden, bevor sie auf die einzelnen Anwendungen ausgerollt werden.</p>

<p>Ein Design System geht weit darüber hinaus zu definieren wie ein Button auszusehen hat. Lasst uns also als nächstes einen Blick darauf werfen, welche Bestandteile in einem Design System enthalten sein sollten.</p>

<h2>Inhalt eines Design Systems</h2>

<p>Begriffe die schon länger existieren sind Corporate Design und Corporate Identity. Damit ist im wesentlichen ein einheitliches Auftreten einer Firma nach außen gemeint. Auch dazu gibt es schon länger entsprechende Richtlinien innerhalb von Firmen, um den Mitarbeitern eine Basis für Kommunikation mit Presse oder Endkunden zu bieten. Bestandteile dieser Richtlinien bilden auch die Basis für die Erstellung eines Design Systems.</p>

<p>Die beiden wichtigsten Definitionen, die wohl auch schon im Rahmen einer Firmengründung entschieden werden sollten, sind Schriftarten und zu verwendende Farben des Unternehmens. Sie sind fester Bestandteil eines jeden Design Systems und auch unabhängig davon für eine Firma von entscheidender Bedeutung.</p>

<p>Ein weiterer Punkt der relativ zu Beginn eines Design Systems festgelegt werden sollte sind Abstände. Dabei sind sowohl die Abstände innerhalb von Elementen, wie Buttons oder Tabellenzellen, als auch zwischen den Elementen zu bestimmen, beispielweise vor oder nach einer Überschrift. Viele Systeme verwenden hier oft ein Vielfaches von 4. Da in der modernen Webentwicklung in CSS oft die <a href="https://www.w3schools.com/cssref/css_units.asp">Einheit <code>rem</code></a> verwendet wird, bieten sich bei einer Basis von 16 Pixeln also Werte in 0.25 Schritten an, z.B. 0.25rem, 0.5rem, 0.75rem usw.</p>

<p>Die bisher genannten Bestandteile gehören zum sog. Style Guide. Ein weiterer wichtiger Teil eines Design Systems beschäftigt sich mit dessen Komponenten. Dieser umfasst das Aussehen der einzelnen Elemente einer Benutzeroberfläche und auch die Anordnung dieser innerhalb von komplexeren Layouts. Auch ist dieser Teil wohl der interessanteste für einen Entwickler.</p>

<p>Die Basis für diese sogenannte Komponenten-Bibliothek bildet dabei der Style Guide, der definiert, welche Schriften in welcher Größe verwendet werden sollen, und welche Farben z.B. Buttons für bestimmte Zustände haben sollen. Darüber hinaus definiert die Bibliothek auch das Aussehen von Elementen wie Buttons, Checkboxen oder Eingabefeldern. Dazu zählen Rahmen um die Elemente, abgerundete Ecken oder auch ein Schatten. Mit Details möchte ich mich an dieser Stelle noch zurückhalten, da das Ganze schnell sehr technisch wird, und wir das später beim Dokumentieren unseres eigenen kleinen Design Systems noch genauer betrachten werden.</p>

<p>Je nachdem wo man sich umsieht, gibt es neben einem Style Guide und der Komponenten-Bibliothek noch weitere Bestandteile, die zu einem Design System gezählt werden können. An dieser Stelle möchte ich deshalb noch Animationen und Übergänge zwischen den Seiten einer Anwendung anführen und es an dieser Stelle gut sein lassen mit den Inhalten eines Design Systems. Wie die Komponenten-Bibliothek im speziellen strukturiert werden kann, erfahrt ihr im nächsten Abschnitt.</p>

<h2>Atomares Design für Beginner</h2>

<p>Nachdem ihr nun wisst, dass es die Komponenten-Bibliothek gibt, schauen wir uns als nächstes an, wie sie strukturiert werden kann. Dazu werde ich mich im Wesentlichen auf einen sehr interessanten <a href="https://atomicdesign.bradfrost.com/chapter-2/">Artikel zum Thema atomares Design</a> von <a href="https://bradfrost.com/">Brad Frost</a> berufen, dessen Inhalt ich hier etwas verkürzt zusammenfassen möchte.</p>

<p>Die Grundidee dahinter ist Folgende: wir beginnen mit den kleinsten Einheiten unseres Designs (den Atomen) und arbeiten uns Stück für Stück weiter voran. Beispiele für Atome sind Buttons und Eingabefelder.</p>

<p>Die nächste Stufe sind dann sog. Moleküle, also einige wenige Elemente in einer Gruppe. Hier finden wir zum Beispiel ein Suchfeld mit einem Button. Wichtig dabei ist, dass die verwendeten Elemente in einer sehr engen Beziehung stehen.</p>

<p>Auf der nächsten Stufe finden wir dann Organismen. Sie vereinen mehrere Atome und/oder Moleküle zu größeren Einheiten. Die Einzelteile haben dabei keine so enge Beziehung wie innerhalb von Molekülen, erfüllen aber doch einen gemeinsamen Zweck. Ein sehr gutes Beispiel dafür ist eine Navigationsleiste. Mehrere voneinander unabhängige Bestandteile dienen trotzdem dem gemeinsamen Ziel, einen Benutzer auf der Seite navigieren zu lassen. Sei es auf einzelne Unterseiten oder aber zu seinem Profil.</p>

<p>Die letzten beiden Teile des atomaren Designs sind Vorlagen und Seiten. Vorlagen bilden dabei eine abstrakte Anordnung aller bisher genannten Einheiten. Die Seiten verwenden dann diese Vorlagen und füllen sie mit Leben bzw. Inhalten.</p>

<p>Wer mehr zu dem Thema wissen will, sollte sich unbedingt den eingangs in diesem Abschnitt verlinkten Artikel durchlesen.</p>

<p>Genug aber mit der grauen Theorie, lasst uns Farbe ins Spiel bringen, und unser eigenes Design System dokumentieren.</p>

<h2>Das eigene Design System</h2>

<p>Da die Erstellung und Dokumentation eines eigenen Design Systems sehr viel Zeit in Anspruch nimmt, muss ich mich hier natürlich auf das Wesentliche beschränken. Ich versuche auf Basis der verwendeten Werkzeuge und Frameworks für die wichtigen Teile jeweils ein Beispiel zu geben. Ihr könnt das für eure eigenen Projekte natürlich beliebig erweitern.</p>

<h3>Werkzeuge und Frameworks</h3>

<p>Im Vorfeld dieses Beitrags habe ich mich ausgiebig mit der Analyse von Werkzeugen zur Dokumentation von Design Systemen befasst. Wie fast überall gibt es dabei bezahlte Angebote, auf die ich hier nicht eingehen werde, aber auch freie Alternativen. Ein sehr gutes Werkzeug der zweiten Kategorie ist mit Sicherheit <a href="https://storybook.js.org/">Storybook</a>.</p>

<p>Da ich aber aus einer Welt komme, in der SPAs (noch) keine Rolle spielen, da grafische Oberflächen nahezu vollständig mit Template Engines auf dem Server erzeugt werden, störte mich dabei die starke Fokussierung auf Web-Komponenten (egal ob HTML5, Vue oder React). Meine Wahl fiel deshalb auf <a href="https://fractal.build/">Fractal</a>. Es bietet einen gröberen Rahmen und lässt uns somit mehr Freiheiten. Wie es der Zufall haben will, ist es damit außerdem möglich, <a href="https://twig.symfony.com/">Twig</a> als Template Sprache zu verwenden, das in der PHP-Welt derzeit wohl das beste ist, was man finden kann. Solltet ihr direkt mit SPAs loslegen, könnt ihr natürlich auch Storybook verwenden. Ich lese durchweg positives darüber und mit jeder Version wird es wohl noch besser.</p>

<p>Als nächstes brauchen wir noch etwas, um unsere Komponenten auch stylen zu können. Hier bin ich aktuell ein Fan von <a href="https://tailwindcss.com/">TailwindCSS</a>. Es ist vor Kurzem in Version 2 erschienen und ist ein sog. Utility Framework. D.h. es gibt nicht, wie z.B. bei Bootstrap üblich, vorgefertigte Organismen (um auf das atomare Design zurückzukommen), sondern lediglich low-level Hilfsklassen, mit denen ihr viel Freiheit genießt, die aber natürlich auch eine gewisse Gefahr mit sich bringt.</p>

<p>Tailwind wird durchaus kontrovers diskutiert, worauf ich hier nicht eingehen will. Wer möchte, kann sich diesen <a href="https://adamwathan.me/css-utility-classes-and-separation-of-concerns/">Blog Post</a> durchlesen, der die Ideen und die Entstehung von Tailwind ausführlich erklärt. Ich will hier einige Funktionen herausstellen, die mich von der Verwendung überzeugt haben:</p>

<ul>
<li>Alles ist konfigurierbar. Dazu zählen z.B. die Farben, Basisgröße der generischen Klassen für Schriftgröße oder Abstände. Auch neue Klassen können per Konfiguration hinzugefügt werden.</li>
<li>Was ist in dem Paket? Nur das Nötigste! Tailwind integriert in den Buildprozess eine Bereinigung, die nicht verwendete Klassen entfernt und somit für eine kleine CSS Datei sorgt.</li>
<li>Abstraktion durch Komponenten. Wenn sich Styles häufig wiederholen, z.B. für die Basis eines Buttons, ist es möglich diese Definitionen über Komponenten zu extrahieren.</li>
<li>&#8220;Mobile first&#8221; responsives Design und Dark Mode inklusive.</li>
<li>Lebhafte Community. Wer nicht das Superauge für Design hat (wie ich zum Beispiel), für den gibt es kostenlose Inspirationsquellen: <a href="https://mertjf.github.io/tailblocks/">Tailblocks</a> und <a href="https://tailwindcomponents.com/">Tailwind Components</a>.</li>
</ul>

<p>Mit Fractal und TailwindCSS haben wir das Fundament gelegt. Im nächsten Abschnitt werden wir die beiden nun vereinen, und das Design System zum Leben erwecken.</p>

<h3>Installation und Einrichtung</h3>

<p>Dieser Abschnitt beschäftigt sich mit der Grundinstallation von Fractal und TailwindCSS. Da Fractal ein <a href="https://nodejs.org/en/">NodeJS</a> Projekt ist, initialisieren wir zuerst unser Projekt. Erstellt dazu irgendwo auf eurer Festplatte einen Ordner namens <code>design-system</code> und öffnet ihn in einem Konsolenfenster. Danach führt folgenden Befehl aus:</p>

<pre><code class="bash">npm init -y
</code></pre>

<p>Wie ihr seht, verwende ich für diesen Artikel <code>npm</code>, ihr könnt aber gerne auch <code>yarn</code> verwenden. Der obige Befehl bestätigt alle Optionen mit den Standardwerten und erstellt ein Grundgerüst einer <code>package.json</code> Datei. Diese ist nötig, um die benötigten Pakete aufzuzeichnen, und die nötigen Konsolenbefehle registrieren zu können. Wenn ihr wollt, könnt ihr auch noch einen <code>git init</code> Befehl ausführen, um das Ganze versionieren zu können. Wer mit GIT noch wenig bis gar keine Erfahrungen hat, sich aber darüber informieren will, dem empfehle ich die Seite von <a href="https://www.atlassian.com/git">Atlassian</a>. Hier werdet ihr leicht verständlich in die Thematik eingeführt.</p>

<p>Als nächstes installieren wir Fractal inklusive Twig Template Plugin in unserem NodeJS Projekt. Führt dazu folgendes Kommando auf der Konsole in eurem Ordner aus:</p>

<pre><code class="bash">npm install --save @frctl/fractal @frctl/twig
</code></pre>

<p>Die Option <code>--save</code> sorgt dafür, dass dieses Paket als Abhängigkeit in der <code>package.json</code> Datei hinzugefügt wird. Solltet ihr GIT verwenden und euer Projekt zu einem späteren Zeitpunkt nochmal installieren wollen, reicht ein <code>git clone</code> und anschließend das Ausführen von <code>npm install</code> im von GIT erstellten Ordner, um den lauffähigen Zustand wiederherzustellen.</p>

<p>Die offizielle Installationsanleitung schlägt als nächstes vor, das Kommandozeilen Werkzeug für Fractal zu installieren. Das werden wir an dieser Stelle nicht machen, da ich kein großer Fan davon bin, nach und nach immer mehr verschiedene Konsolenkommandos global auf einem Rechner zu installieren, solange es nicht nötig ist.</p>

<p>Um Fractal trotzdem starten zu können, fügen wir in den <code>scripts</code> Abschnitt innerhalb der <code>package.json</code> Datei folgende Zeile ein:</p>

<pre><code class="json">"scripts": &#123;
  "start": "fractal start --sync"
&#125;,
</code></pre>

<p>Das dort existierende <code>test</code> Kommando könnt ihr löschen.</p>

<p>Bevor wir den Fractal Entwicklungsmodus starten können, benötigen wir noch eine Projektkonfiguration. Legt dazu eine Datei namens <code>fractal.config.js</code> mit folgendem Inhalt an:</p>

<pre><code class="javascript">'use strict'

const fractal = (module.exports = require('@frctl/fractal').create())
</code></pre>

<p>Die hier erstellte Instanz werden wir im nächsten Abschnitt verwenden, um Fractal noch weiter unseren Bedürfnissen anzupassen. Für den Moment reicht diese Konfiguration aber aus, um den Server zu starten.</p>

<p>Wenn ihr also auf der Konsole das nachfolgende Kommando ausführt, wird der Entwicklungsserver von Fractal gestartet:</p>

<pre><code class="bash">npm start
</code></pre>

<p>In der Konsole werden euch dann verschiedene URLs angezeigt, mit denen ihr die Seite im Browser öffnen könnt. In meine Fall verwende ich dazu <a href="http://localhost:3000/">http://localhost:3000/</a>. Nach dem Öffnen seht ihr eine Platzhalterseite, da wir selbst noch keine Inhalte angelegt haben. Bevor wir damit aber im nächsten Abschnitt fortfahren, lasst uns noch TailwindCSS installieren und in Fractal integrieren. Führt dazu auf der Konsole erneut ein entsprechendes Kommando aus:</p>

<pre><code class="bash">npm install --save tailwindcss autoprefixer postcss
</code></pre>

<p>Auf der offiziellen Installationsanleitung sind verschiedene Möglichkeiten dokumentiert, wie Tailwind konfiguriert werden kann. Solltet ihr an den Details Interesse haben, könnt ihr euch <a href="https://tailwindcss.com/docs/installation#installing-tailwind-css-as-a-post-css-plugin">hier</a> informieren. Ich werde nur das nötigste davon anführen, da es hier primär um Fractal gehen soll, und TailwindCSS lediglich lauffähig sein muss. Ich könnte mindestens einen separaten Beitrag über TailwindCSS alleine schreiben.</p>

<p>Nachdem der oben gezeigte Befehl fertig ausgeführt ist, müssen wir zunächst einige Ordner und Dateien anlegen. Hier eine Liste der Ordner und Dateien, die ihr zunächst anlegen müsst:</p>

<pre><code>/tailwind.config.js
/public/
/src/tailwind/main.css
</code></pre>

<p>Über <code>tailwind.config.js</code> lässt sich Tailwind euren Wünschen anpassen. Wir verwenden an dieser Stelle die Standard-Konfiguration, d.h. ihr müsst einfach den folgenden Inhalt in diese Datei kopieren:</p>

<pre><code class="javascript">module.exports = &#123;
  purge: &#91;&#93;,
  darkMode: false, // or 'media' or 'class'
  theme: &#123;
    extend: &#123;&#125;
  &#125;,
  variants: &#123;&#125;,
  plugins: &#91;&#93;
&#125;
</code></pre>

<p>Im Ordner <code>public</code> wird die von Tailwind erzeugte Ausgabedatei angelegt bzw. bei Änderungen überschrieben. Diesen Ordner werden wir später in Fractal als Ordner für statische Assets angeben.</p>

<p>In die Datei <code>src/tailwind/main.css</code> müsst ihr nun noch folgenden Inhalt einfügen, welcher auch dem Standard von Tailwind entspricht:</p>

<pre><code class="css">@tailwind base;
@tailwind components;
@tailwind utilities;
</code></pre>

<p>Um nun die Tailwind Konfiguration zu einer fertigen CSS Datei konvertieren zu können, benötigen wir ein weiteres Skript in der <code>package.json</code> Datei:</p>

<pre><code class="json">"scripts": &#123;
  "start": "fractal start --sync",
  "build:tailwind": "tailwind build src/tailwind/main.css -o public/style.css"
&#125;
</code></pre>

<p>Führt nun auf der Konsole folgendes Kommando aus, um das erste Mal eine CSS Datei erzeugen zu lassen:</p>

<pre><code class="bash">npm run build:tailwind
</code></pre>

<p>Im <code>public</code> Ordner sollte jetzt eine Datei namens <code>style.css</code> existieren, die wir im nächsten Abschnitt dann Fractal bekannt machen werden.</p>

<p><strong>Wichtig:</strong> ich habe hier bewusst auf einen <em>watch</em> Modus für Tailwind verzichtet. D.h. wenn ihr etwas in den Dateien <code>tailwind.config.js</code> oder <code>main.css</code> ändert, müsst ihr dieses Kommando erneut ausführen, um die Datei <code>style.css</code> neu erstellen zu lassen.</p>

<h3>Konfiguration von Fractal</h3>

<p>Nachdem nun alle nötigen Komponenten installiert und soweit nötig konfiguriert und generiert wurden, lasst uns als nächstes das Herzstück unseres Design Systems konfigurieren, nämlich Fractal. Dazu haben wir bereits zuvor die Datei <code>fractal.config.js</code> angelegt, die im folgenden Abschnitt mit Inhalt gefüllt werden soll.</p>

<p>Kopiert den folgenden Inhalt an das Ende der Konfigurationsdatei, ich werde direkt im Anschluss daran auf die einzelnen Bestandteile eingehen:</p>

<pre><code class="javascript">const twigAdapter = require('@frctl/twig')()

/* Set the title of the project */
fractal.set('project.title', 'Generic Design System')
fractal.set('project.version', '1.0.0')
fractal.set('project.author', 'Generic Company')

/* Tell Fractal where the components will live */
fractal.components.set('path', __dirname + '/src/components')
fractal.components.engine(twigAdapter)
fractal.components.set('ext', '.twig')

/* Define the wrapper of the component preview */
fractal.components.set('default.preview', '@preview')

/* Tell Fractal where the documentation pages will live */
fractal.docs.set('path', __dirname + '/src/docs')
fractal.docs.set('label', 'Design System')

/* Specify a directory of static assets */
fractal.web.set('static.path', __dirname + '/public')

/* Set the static HTML build destination */
fractal.web.set('builder.dest', __dirname + '/build/docs')
</code></pre>

<p>Wie erwähnt, werden wir Twig als Template Engine nutzen. Diese Tatsache müssen wir Fractal entsprechend mitteilen. Dazu wird zunächst eine Instanz davon in der Variable <code>twigAdapter</code> erstellt und später für das Rendering der Komponenten registriert. Außerdem wird die Dateiendung auf <code>.twig</code> gestellt:</p>

<pre><code class="javascript">const twigAdapter = require('@frctl/twig')()
fractal.components.engine(twigAdapter)
fractal.components.set('ext', '.twig')
</code></pre>

<p>Es folgen einige allgemeine Projektinformationen, die ihr natürlich nach Belieben ändern könnt:</p>

<pre><code class="javascript">fractal.set('project.title', 'Generic Design System')
fractal.set('project.version', '1.0.0')
fractal.set('project.author', 'Generic Company')
</code></pre>

<p>Diese Informationen bestimmen z.B. den Seitentitel, können aber auch später innerhalb der einzelnen Komponenten ausgelesen und verwendet werden.</p>

<p>Als nächstes müsst ihr noch weitere Ordner anlegen, da diese für die Seiten der Komponenten und für die Seiten des Style Guides benötigt werden:</p>

<pre><code>/src/components/
/src/docs/
</code></pre>

<p>Der Vollständigkeit halber, hier die Konfiguration, die die Ordner entsprechend registriert:</p>

<pre><code class="javascript">fractal.components.set('path', __dirname + '/src/components')
fractal.docs.set('path', __dirname + '/src/docs')
</code></pre>

<p>Zum Abschluss noch die Zeilen, die für die statischen Assets und das Ausgabeverzeichnis der Dokumentation benötigt werden:</p>

<pre><code class="javascript">fractal.web.set('static.path', __dirname + '/public')
fractal.web.set('builder.dest', __dirname + '/build/docs')
</code></pre>

<p>Nun fügen wir noch ein letztes Kommando in die <code>scripts</code> Sektion unserer <code>package.json</code> Datei hinzu, um die Dokumentation für die produktive Version erstellen zu können:</p>

<pre><code class="json">"scripts": &#123;
  "start": "fractal start --sync",
  "build:doc": "fractal build",
  "build:tailwind": "tailwind build src/tailwind/main.css -o public/style.css"
&#125;
</code></pre>

<p>Mit <code>npm run build:doc</code> wird ein produktiver Build unseres Projekts im Ordner <code>build</code> erstellt. Dieser Ordner beinhaltet alles, was ihr benötigt, um die Seite online zu stellen.</p>

<p>Wie ihr vielleicht bemerkt habt, haben wir zwar die TailwindCSS Datei zuvor erzeugt, bisher weiß aber Fractal nichts von dieser Datei. Das erledigen wir mit einer letzten Konfiguration, die ihr ans Ende von <code>fractal.config.js</code> einfügen könnt:</p>

<pre><code class="javascript">const mandelbrot = require('@frctl/mandelbrot')

const myCustomisedTheme = mandelbrot(&#123;
  skin: 'navy',
  nav: &#91;'search', 'docs', 'components'&#93;,
  styles: &#91;'default', '/style.css'&#93;
&#125;)

fractal.web.theme(myCustomisedTheme)
</code></pre>

<p>Gleichzeitig seht ihr hier, wie ihr ein anderes Farbschema (navy) verwenden könnt, und wie die Reihenfolge der Abschnitte der Seitenleiste geändert werden können. In unserem Fall kommt hier zunächst das Suchfeld, dann der Inhalt des <code>docs</code> Ordners, worüber wir den Style Guide erstellen werden, und zum Schluss die Komponenten, welche wir im Ordner <code>components</code> erstellen werden.</p>

<p>Kleiner Nachtrag zum Thema Stylesheet. Um die erzeugte <code>style.css</code> Datei auch innerhalb der Komponentenvorschau verwenden zu können, gibt es in der oben gezeigten Konfiguration eine wichtige Zeile:</p>

<pre><code class="javascript">fractal.components.set('default.preview', '@preview')
</code></pre>

<p>Das definiert eine Datei mit HTML-Grundgerüst, die wir später beim Erstellen unserer Komponenten-Bibliothek anlegen werden.</p>

<p>Die Einrichtung aller Bestandteile wäre damit abgeschlossen. Ihr könnt den Zwischenstand nochmal über <code>npm start</code> im Browser begutachten, bevor wir nun an das Erstellen der Inhalte gehen werden. Am besten lasst ihr dieses Kommando ab sofort dauerhaft laufen, damit sich eure Dokumentation nach jeder Änderung automatisch aktualisiert.</p>

<h3>Style Guide</h3>

<p>Neue Seiten in den Style Guide einzufügen ist sehr einfach. Ihr müsst dazu lediglich eine Datei mit der Endung <code>.md</code> innerhalb des <code>docs</code> Ordners anlegen. Um Abschnitte zu erstellen, können wir auch Unterordner anlegen.</p>

<p>Warum beginnen wir nicht damit, eine Einstiegsseite zu erstellen. Nennt die Datei <code>index.md</code> und fügt folgenden Inhalt ein:</p>

<pre><code class="markdown">---
title: Übersicht
label: Übersicht
---

Das ist unser eigenes Design System.
</code></pre>

<p>Am Anfang der Datei kann jeweils ein Bereich namens <a href="https://fractal.build/guide/documentation/configuration-reference.html#page-properties">YAML front-matter</a> eingefügt werden, in dem ihr einige Seiteneinstellungen definieren könnt. Klickt auf den Link, um zu sehen welche Einstellungen möglich sind. In unserem Beispiel habe ich den Titel der Seite definiert, und über das Label festgelegt, wie die Seite in der linken Navigationsleiste angezeigt wird.</p>

<p>Um die einzelnen Punkte des Style Guides zu gruppieren, legt ihr als nächstes einen Ordner namens <code>style-guide</code> unter <code>docs</code> an. Darin erstellt ihr eine Datei mit dem Namen <code>colors.md</code> mit folgendem Inhalt:</p>

<pre><code class="md">---
title: Farben
label: Farben
---

Hier eine Übersicht unserer Farben

&lt;h1&gt;Blau&lt;/h1&gt;

&lt;div class="w-full h-10 flex items-center bg-blue-400 pl-2 text-white"&gt;bg-blue-400&lt;/div&gt;
&lt;div class="w-full h-10 flex items-center bg-blue-500 pl-2 text-white"&gt;bg-blue-500&lt;/div&gt;

&lt;h1&gt;Rot&lt;/h1&gt;

&lt;div class="w-full h-10 flex items-center bg-red-400"&gt;&lt;/div&gt;
&lt;div class="w-full h-10 flex items-center bg-red-500"&gt;&lt;/div&gt;

&lt;h1&gt;Grün&lt;/h1&gt;
&lt;div class="w-full h-10 flex items-center bg-green-400"&gt;&lt;/div&gt;
&lt;div class="w-full h-10 flex items-center bg-green-500"&gt;&lt;/div&gt;
</code></pre>

<p>Schaut euch das Ergebnis im Browser an!</p>

<p>Da Markdown intern zu HTML konvertiert wird, ist es möglich, direkt innerhalb der Dateien eigenes HTML Markup zu schreiben. Hier seht ihr auch die Utility Klassen von TailwindCSS im Einsatz. Ich habe beispielhaft für die beiden blauen Boxen auch den Namen der verwendeten Klassen hinzugefügt, dass z.B. ein Entwickler sich direkt die Definition kopieren kann.</p>

<p>Ich denke als Einstieg ist das erst einmal ausreichend. Alle weiteren Möglichkeiten für Dokumentationsseiten findet ihr in der <a href="https://fractal.build/guide/documentation/">offiziellen Dokumentation</a>.</p>

<h3>Komponenten-Bibliothek</h3>

<p>Kommen wir nun zum Herzstück des Design Systems, der Komponenten-Bibliothek. Auch hier gelten ähnliche Konventionen wie zuvor. Seiten die ihr direkt im Verzeichnis <code>components</code> anlegt, erscheinen auf der Hauptebene der Navigation, während ihr mit Unterverzeichnissen weitere Ebenen erstellen könnt.</p>

<p>Bevor wir mit einer Beispielkomponente beginnen, möchte ich auf die zuvor erwähnte Datei mit dem HTML-Grundgerüst zurückkommen, die wir benötigen um TailwindCSS für die Vorschau der Komponenten laden zu können. Erstellt dazu eine Datei namens <code>_preview.twig</code> im Ordner <code>components</code> und fügt folgenden Inhalt ein:</p>

<pre><code class="html">&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;link rel="stylesheet" href="&#123;&#123; '/style.css'|path &#125;&#125;" /&gt;
    &lt;title&gt;Preview&lt;/title&gt;
  &lt;/head&gt;
  &lt;body class="bg-white p-4"&gt;
    &#123;&#123; yield | raw &#125;&#125;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>

<p>Wie ihr sehen könnt, wird unsere CSS Datei eingebunden, und auf den <code>body</code> ein weißer Hintergrund und etwas Abstand zu den Rändern mittels <code>padding</code> hinzugefügt. Der Unterstrich im Dateinamen sorgt übrigens dafür, dass die Seite nicht als normale Komponente interpretiert wird.</p>

<h4>Komponente: Infobox</h4>

<p>Als erstes möchte ich euch eine Komponente für Infoboxen zeigen. Erstellt dazu die Datei <code>alert.twig</code> direkt im <code>components</code> Ordner, mit folgendem Inhalt:</p>

<pre><code class="twig">&lt;div
  class="p-3 bg-&#123;&#123;modifier&#125;&#125;-200 border-l-4 border-&#123;&#123;modifier&#125;&#125;-900 text-&#123;&#123;modifier&#125;&#125;-900 flex items-center relative"
&gt;
  &lt;span class="text-4xl mr-4"&gt;
    &lt;i class="fa fa-warning text-4xl"&gt;&lt;/i&gt;
  &lt;/span&gt;
  &lt;span&gt;
    &lt;span class="block mb-2 text-2xl"&gt;
      &#123;&#123;title&#125;&#125;
    &lt;/span&gt;
    &lt;span class="text-base"&gt;
      &#123;&#123; message &#125;&#125;
    &lt;/span&gt;
  &lt;/span&gt;
&lt;/div&gt;
</code></pre>

<p>Ihr seht hier im Markup einige Twig Platzhalter, z.B. <code>&#123;&#123;modifier&#125;&#125;</code>. Das schöne an Fractal ist, ihr könnt <strong>ein Markup</strong> für <strong>mehrere Ausprägungen</strong> einer Komponente schreiben. Um diese Platzhalter dann mit Inhalten zu füllen, gibt es Konfigurationsdateien. Für dieses Beispiel könnt ihr dazu im Ordner <code>components</code> eine Datei mit dem Namen <code>alert.config.json</code> erstellen und darin folgenden Inhalt einfügen:</p>

<pre><code class="json">&#123;
  "title": "Alert message",
  "status": "1_0_0",
  "context": &#123;
    "modifier": "blue",
    "message": "This is the info message"
  &#125;,
  "variants": &#91;
    &#123;
      "name": "default",
      "label": "Info",
      "context": &#123;
        "modifier": "blue",
        "title": "Info",
        "name": "info"
      &#125;
    &#125;,
    &#123;
      "name": "success",
      "context": &#123;
        "modifier": "green",
        "message": "This is a success message",
        "title": "Success",
        "name": "success"
      &#125;
    &#125;
  &#93;
&#125;
</code></pre>

<p>Wenn ihr euch die Dokumentation jetzt im Browser anschaut, solltet ihr in der linken Navigationsleiste sehen, dass es pro Ausprägung einen eigenen Menüeintrag gibt. Klickt euch ruhig etwas durch die einzelnen Reiter, das habt ihr euch mittlerweile verdient.</p>

<p>Entwickler können sich hier später auch den erzeugten Quellcode einfach rauskopieren, somit sieht eine Erfolgsmeldung immer gleich aus. Wie kurz bei den Erläuterungen zu TailwindCSS erwähnt, könnte man die einzelnen Definitionen auch als Tailwind Komponente extrahieren. Dadurch wären nicht so viele Utility Klassen im Markup nötig.</p>

<h4>Komponente: Button</h4>

<p>Ihr habt es fast geschafft, haltet nur noch ein wenig durch. Zum Abschluss möchte ich euch noch eine Button Komponente zeigen, und zwar in zweifacher Ausführung. Einmal werden wir verschiedene Farben verwenden, einmal verschiedene Größen umsetzen.</p>

<h5>Button: Verschiedene Farben</h5>

<p>Dieses Beispiel geht in die Richtung der zuvor gezeigten Infobox Komponente. Erstellt zunächst im Ordner <code>components</code> einen Unterordner <code>button</code>. Darin erstellt ihr einen weiteren Unterordner namens <code>01-styles</code>. Die <code>01-</code> im Ordnernamen kann man in Fractal nutzen, um manuell eine Reihenfolge von Komponenten zu definieren. Das funktioniert sowohl für Datei- als auch Ordnernamen. Die Nummer wird später in der Oberfläche nicht angezeigt.</p>

<p>Legt als nächstes die Datei <code>styles.twig</code> im zuvor erstellen Unterordner an und fügt folgenden Inhalt ein:</p>

<pre><code class="twig">&lt;button class="px-4 py-2 bg-&#123;&#123;modifier&#125;&#125;-800 text-white hover:bg-&#123;&#123;modifier&#125;&#125;-700"&gt;
    &#123;&#123;title&#125;&#125;
&lt;/button&gt;
</code></pre>

<p>Auch hier seht ihr wieder Platzhalter, die gefüllt werden wollen. Deshalb solltet ihr eine Datei namens <code>styles.config.json</code> anlegen und dort folgende Konfiguration einfügen:</p>

<pre><code class="json">&#123;
  "title": "Button",
  "status": "1_0_0",
  "context": &#123;
    "modifier": "blue"
  &#125;,
  "handle": "button",
  "default": "info",
  "variants": &#91;
    &#123;
      "name": "info",
      "context": &#123;
        "modifier": "blue",
        "title": "Show more"
      &#125;
    &#125;,
    &#123;
      "name": "success",
      "context": &#123;
        "modifier": "green",
        "title": "OK"
      &#125;
    &#125;
  &#93;
&#125;
</code></pre>

<h5>Button: Verschiedene Größen</h5>

<p>Nun zum letzten Beispiel. Da ihr das Vorgehen mittlerweile kennt, hier die Anweisungen, die ihr strikt befolgen müsst!</p>

<p>Erstellt im <code>button</code> Ordner einen neuen Unterordner und nennt ihn <code>02-sizes</code>. Darin erstellt ihr jetzt die Datei <code>sizes.twig</code> mit dem Inhalt:</p>

<pre><code class="twig">&lt;button class="px-&#123;&#123;px&#125;&#125; py-&#123;&#123;py&#125;&#125; text-&#123;&#123;text&#125;&#125; bg-blue-800 text-white hover:bg-blue-700"&gt;
    &#123;&#123;title&#125;&#125;
&lt;/button&gt;
</code></pre>

<p>Als letztes fehlt noch die Konfiguration für diese Komponente, die ihr in der Datei <code>sizes.config.json</code> mit folgendem Inhalt definiert:</p>

<pre><code class="json">&#123;
  "title": "Button",
  "status": "1_0_0",
  "context": &#123;
    "modifier": "blue"
  &#125;,
  "collated": true,
  "handle": "button-size",
  "default": "small",
  "variants": &#91;
    &#123;
      "name": "small",
      "label": "Small",
      "context": &#123;
        "title": "Small",
        "text": "sm",
        "px": "2",
        "py": "0"
      &#125;
    &#125;,
    &#123;
      "name": "normal",
      "context": &#123;
        "title": "Normal",
        "text": "base",
        "px": "4",
        "py": "1"
      &#125;
    &#125;,
    &#123;
      "name": "large",
      "context": &#123;
        "title": "Large",
        "text": "xl",
        "px": "6",
        "py": "2"
      &#125;
    &#125;
  &#93;
&#125;

</code></pre>

<p>Das wars! Ihr habt es geschafft! Wenn ihr mich bis hier hin verfolgt habt, dann sollte das fertige Ergebnis in etwa so aussehen:</p>

<p><img src="/images/uploads/design-system-final.jpg" alt="Fertiges Design System" /></p>

<p>In der Konfiguration der letzten Komponente seht ihr übrigens <code>"collated": true</code>, was dafür sorgt, dass die Ausprägungen alle auf der gleichen Seite zu finden sind, nicht auf einzelnen Unterseiten.</p>

<h2>Fazit</h2>

<p>Obwohl dieser Beitrag etwas länger geworden ist als ursprünglich vermutet, kratzt er natürlich nur an der Oberfläche der Themen. Ich hoffe, ich konnte euch trotzdem einen spannenden Einblick geben, und euch dafür begeistern, euch das Ganze selbst ausführlicher anzusehen. Ich freue mich bereits auf die beruflichen Herausforderungen der nächsten Monate und wünsche euch viel Spaß bei der Erstellung eures eigenen Design Systems auf Basis der hier gezeigten Werkzeuge.</p>

<p>Nun will ich euch aber nicht länger aufhalten. Bis bald auf Next Direction!</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Ein Entwickler, zwei Welten]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/ein-entwickler-zwischen-zwei-welten" />
      <id>tag:https:2020:/next-direction.de/2.21</id>
      <published>2020-11-21T15:00:00Z</published>
      <updated>2020-11-22T07:11:41Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/zwei-welten.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/zwei-welten.jpg" alt="Zwei Welten"/><br>
        <p>Willkommen zurück! In diesem Beitrag will ich euch von Anfang bis Ende mit meiner derzeitigen Entwicklungsumgebung vertraut machen. Das heißt, wir werden bei der Installation des Windows Subsystems für Linux in der Version 2 (WSL2) beginnen, und aufhören wenn alle Dienste automatisch starten. Als Bonus lassen sich mit diesem Setup auch Linux Anwendungen mit grafischer Oberfläche in Windows starten.</p>

<h3>Vorgeschichte</h3>

<p>Falls ihr euch fragt, wie es zu diesem Umstand gekommen ist, dann lest weiterhin diesen Abschnitt. Sollten euch nur harte Fakten bzw. Installationsschritte interessieren, könnt ihr die folgenden Zeilen auch überspringen.</p>

<p>Bis Ende 2019 war ich komplett auf Windows unterwegs und sah auch nie die Notwendigkeit Linux oder gar einen Mac auszuprobieren. Windows 10 ist für mich eigentlich auch heute noch das perfekte Betriebssystem, mit dem ich alles machen kann was ich will. Auch wenn ich nie der extreme Zocker war, waren mir kleine Spiele für Zwischendurch immer wichtig. Leider ist es auch heute noch schwierig manche Spiele auf Linux zum Laufen zu bringen, wodurch ich Linux nie als Hauptsystem für meinen PC in Betracht gezogen habe. Natürlich habe ich immer wieder Mal einen Bastelrechner damit installiert und etwas ausprobiert, aber auf den produktiven Stand wie mit Windows kam ich damit nie.</p>

<p>Als ich dann beruflich etliche Probleme mit der verbauten Grafikkarte bzw. dem dazugehörigen Treiber unter Windows bekam, und täglich mehrere Programme willkürlich abstürzten, dachte ich mir, dass ich Linux mal eine Chance geben sollte. Nach etlichen Installationen diverser Distributionen landete ich schließlich bei Kubuntu. Ubuntu, Linux Mint und auch Fedora hatten immer Probleme mit der Skalierung von Anwendungen auf einem UHD Monitor.</p>

<p>Nachdem ich mit dieser Konstellation beruflich sehr zufrieden bin, käme es aufgrund der angesprochenen Spiele Thematik trotzdem privat nicht infrage.</p>

<h5>Beweggründe</h5>

<p>Kommen wir als nächstes also zur Zusammenfassung, wieso ich mich nun dazu entschieden habe, ein Setup auf Basis von WSL2 einzurichten.</p>

<p>Nachdem ich mehrere Monate mit einem Linux System gearbeitet habe, lernte ich doch einige Punkte zu schätzen, aber es gab auch Sachen die mich stören. Die folgenden Auflistungen beziehen sich überwiegend auf den täglichen Arbeitsablauf meiner aktuellen Arbeitsstelle.</p>

<h6>Vorteile einer Linux-Umgebung</h6>

<p>Ich war immer schon ein Fan der Linux Konsole, speziell von der Möglichkeit über eine <strong>Paketverwaltung</strong> schnell und einfach benötigte Programme zu installieren. Auch wenn es in der Windowswelt Ansätze dafür gibt, fühlt sich das einfach nicht gleich an.</p>

<p>Die meisten <strong>Werkzeuge und Programme</strong> für die Entwicklung gibt es <strong>nativ für Linux</strong>. Vor allem bei exotischeren Bibliotheken ist es öfter mal der Fall, dass es keine vorkompilierten Versionen für Windows gibt. Wer schon mal eine ausführbare Datei z.B. mit Cygwin kompiliert hat, der weiß sicher was ich meine.</p>

<p>Einer der schönsten Vorteile ist für mich die <strong>Arbeitsgeschwindigkeit</strong>, besonders in Verbindung mit <strong>Git Operationen</strong>. Beruflich und auch privat verwende ich als Git Client &#8220;GitKraken&#8221;. Während es unter Windows schonmal 30-60 Sekunden für einen Klon braucht, passiert das Ganze unter Linux in ca. 10 Sekunden. Über die Konsole ist es sogar noch schneller. Auch das Wechseln von Branches passiert in einem Bruchteil der Zeit.</p>

<h6>Nachteil einer Linux-Umgebung</h6>

<p>Ja ihr lest richtig, für mich gibt es zumindest beruflich nur <strong>einen</strong> entscheidenden Nachteil, wenn ich mit Linux arbeite. Ich bin ein Fan von Microsoft Office und Outlook im speziellen, wenn es um die Arbeit geht. Obwohl Evolution als E-Mail Client meiner Wahl im Großen und Ganzen funktioniert, gibt es oft kleinere Probleme die auf Dauer stören:</p>

<ul>
<li>E-Mails werden S/Mime verschlüsselt. An manchen Tagen dauert es dann schon mal 1-2 Minuten, bis eine E-Mail verschickt wird.</li>
<li>Aufgaben und Termine zu verwalten ist um Längen schlechter als mit Outlook.</li>
<li>Es kommt öfter mal vor, dass Kalender nicht synchronisiert werden können.</li>
</ul>

<p>Zeit also, das beste aus beiden Welten zu vereinen, und mit der Installation der einzelnen Bestandteile zu beginnen.</p>

<h3>Die Komponenten</h3>

<p>Ich habe für die Installation der diversen Komponenten einiges an Recherche betrieben, da ich mich erst zufrieden geben wollte, nachdem alles perfekt funktionierte und zusammenspielte. Hier zunächst eine Liste der Sachen, die wir installieren und konfigurieren werden:</p>

<ul>
<li>Windows Subsystem für Linux 2 mit Debian</li>
<li>Windows Terminal</li>
<li>Apache2, MariaDB und PHP inklusive Autostart in WSL2</li>
<li>XServer für Windows 10 inklusive Autostart</li>
<li>GitKraken und PHPStorm in WSL2 inklusive Windows Verknüpfungen zum Starten</li>
</ul>

<p>Die Grundinstallation von Windows 10 setze ich hier voraus. Ich werde auch nicht darauf eingehen, wie ihr Office installiert 😉.</p>

<h5>WSL2</h5>

<p>Das Herzstück der hier vorgestellten Lösung ist natürlich das Windows Subsystem für Linux. Nachdem ich die Entwicklung der ersten Version nur am Rande verfolgt habe und es wohl auch einige Probleme damit gab, bin ich froh, dass Microsoft mit Version 2 den Ansatz geändert hat. Hatte man bei Version 1 noch versucht, eine eigene Kompatibilitätsschicht zwischen dem eigenen und dem Linux Kernel zu schaffen, setzt man in Version 2 einfach auf eine virtuelle Maschine. Diese wird so weit möglich nahtlos in Windows integriert. Ich habe auch schon einen Blogeintrag von Microsoft gelesen, der einen der hier beschriebenen Schritte überflüssig macht. Microsoft will nämlich einen eigenen XServer anbieten, um Desktopanwendungen aus der WSL starten zu können.</p>

<p>Da sich die Installation in der Vergangenheit mehrfach geändert hat, möchte ich an dieser Stelle die <a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10">offizielle Anleitung von Microsoft</a> verlinken und die derzeitigen Schritte nur kurz zusammenfassen.</p>

<p>Die ersten Schritte befassen sich mit der Installation der systemseitigen Voraussetzungen. Zum jetzigen Zeitpunkt (November 2020) ist es noch nötig, nach Aktivierung des Windows Subsystems für Linux ein Upgrade auf Version 2 auszuführen. Ich gehe davon aus, die langfristige Lösung wird sein, eine direkte Installation für die Version 2 anzubieten. Lassen wir uns also überraschen.</p>

<p>Nachdem die Grundinstallation abgeschlossen ist und ihr Version 2 als Standard konfiguriert habt, empfehle ich einmal den Rechner neu zu starten, bevor ihr Debian aus dem Microsoft Store installiert.</p>

<p>Ist Debian installiert, könnt ihr es normal über das Startmenü ausführen. Der erste Start nimmt etwas Zeit in Anspruch und ihr werdet am Ende nach einem Benutzernamen und dem dazugehörigen Passwort gefragt. Hier müsst ihr nicht die Daten eures Windows Systems verwenden, sondern könnt komplett neue Daten verwenden. Im Anschluss seid ihr dann auch schon auf der Linux Konsole!</p>

<h5>Windows Terminal</h5>

<p>Nachdem nun Debian installiert ist, gibt es unter Windows 10 mindestens drei verschiedene Kommandozeilen. Einmal die klassische, die ihr mit <code>cmd</code> starten könnt, dann die Powershell, und neu ist die Linux Konsole bzw. Debian, was sich auch normal über das Startmenü ausführen lässt.</p>

<p>Ich weiß nicht wie es euch geht, aber es wäre doch schön, wenn wir alles unter einem Dach vereinen könnten, und auch festlegen könnten welche denn der Standard sein soll. Zum Glück ist das mit der Microsoft Store App &#8220;Windows Terminal&#8221; möglich. Installiert diese also als nächstes aus dem Store und lest weiter, wenn die Installation abgeschlossen ist.</p>

<p>Wenn ihr das Windows Terminal das erste Mal startet, wird Debian nicht der Standard sein. Bevor ich euch aber zeige, wie sich das ändern lässt, will ich euch kurz einige Vorteile nennen, die für mich den Einsatz des Windows Terminals rechtfertigen:</p>

<ul>
<li>Mehrere aktive Sitzungen, auch verschiedene Kommandozeilen möglich</li>
<li>Standardkonsole konfigurierbar</li>
<li>Startverzeichnis konfigurierbar</li>
</ul>

<p>Lasst uns nun also zunächst Debian als Standard festlegen. Dazu öffnet ihr über den Pfeil nach unten, der sich neben den Konsolentabs befindet, die Einstellungen. Nicht erschrecken, es öffnet sich daraufhin eine JSON Datei in eurem Standardeditor. <br>
Alternativ könnt ihr auch <code>Strg + ,</code> drücken.</p>

<p>Hier ein Auszug davon aus einem meiner Systeme:</p>

<pre><code class="json">"list": &#91;
  &#123;
    // Make changes here to the powershell.exe profile.
    "guid": "&#123;61c54bbd-c2c6-5271-96e7-009a87ff44bf&#125;",
    "name": "Windows PowerShell",
    "commandline": "powershell.exe",
    "hidden": false
  &#125;,
  &#123;
    // Make changes here to the cmd.exe profile.
    "guid": "&#123;0caa0dad-35be-5f56-a8ff-afceeeaa6101&#125;",
    "name": "Eingabeaufforderung",
    "commandline": "cmd.exe",
    "hidden": false
  &#125;,
  &#123;
    "guid": "&#123;07b52e3e-de2c-5db4-bd2d-ba144ed6c273&#125;",
    "hidden": false,
    "name": "Debian",
    "source": "Windows.Terminal.Wsl",
  &#125;,
  &#123;
    "guid": "&#123;b453ae62-4e3d-5e58-b989-0a998ec441b8&#125;",
    "hidden": false,
    "name": "Azure Cloud Shell",
    "source": "Windows.Terminal.Azure"
  &#125;
&#93;
</code></pre>

<p>Ab etwa Zeile 30 seht ihr die obige Auflistung aller Konsolen die in euerem System erkannt wurden. Eine davon sollte <strong>Debian</strong> sein. Was ihr davon braucht ist die <code>guid</code>. Diese könnt ihr kopieren und am Anfang der Datei als Wert für <code>defaultProfile</code> angeben. Das sollte dann in etwa so aussehen:</p>

<pre><code class="json">"defaultProfile": "&#123;07b52e3e-de2c-5db4-bd2d-ba144ed6c273&#125;"
</code></pre>

<p>Ab dem nächsten Start des Windows Terminals werdet ihr nun von der Debian Kommandozeile begrüßt. Glückwunsch, dass ihr es bis hierhin geschafft habt!</p>

<p>Später werden wir noch das Startverzeichnis konfigurieren, dazu ist aber zunächst die Installation einiger Pakete notwendig, um das nötige Verzeichnis erstellen zu lassen.</p>

<h5>AMP Dienste</h5>

<p>Da ich überwiegend in der Webentwicklung unterwegs bin, werden wir zu Demonstrationszwecken den klassischen <strong>AMP</strong> Stack installieren. Wir werden es an dieser Stelle einfach halten und lediglich die Grundinstallation für <strong>A</strong>pache2, <strong>M</strong>ariaDB und <strong>P</strong>HP 7.3 vornehmen. Eventuelle Konfigurationen, Module oder Erweiterungen könnt ihr selber nach Bedarf installieren.</p>

<p>Startet also das Windows Terminal mit Debian Konsole und führt dort folgenden Befehl aus:</p>

<pre><code class="bash">sudo apt install apache2 mariadb-server php7.3-common
</code></pre>

<p>Nachdem ihr das bei der Einrichtung vergebene Passwort eingegeben habt, ist die Installation nach kurzer Zeit abgeschlossen. Anders als bei einer normalen Installation von Debian, starten Dienste in der WSL nicht automatisch. Zu Testzwecken, könnt ihr deshalb Apache über den folgenden Befehl einmal manuell starten:</p>

<pre><code class="bash">sudo service apache2 start
</code></pre>

<p>Vorausgesetzt, ihr habt in Windows nicht auch einen Webserver (z.B. XAMPP) laufen, könnt ihr über <code>http://localhost</code> in eurem Windows Standardbrowser nun die Startseite eures Apache Webservers sehen!</p>

<p>Wie ihr seht, könnt ihr auf Dienste der WSL über <code>localhost</code> zugreifen. Ich möchte deshalb an dieser Stelle auf einen wichtigen Punkt hinweisen, den ihr beachten müsst, wenn ihr WSL2 verwendet. Alle Pakete die Ports veröffentlichen, teilen sich diese mit euerem Windows System. Habt ihr also Windows Dienste, die Ports belegen, die auch von den Paketen in der WSL benötigt werden, starten diese nicht.</p>

<h6>Autostart</h6>

<p>Da es ziemlich umständlich wäre, die nötigen Dienste in der WSL bei jedem Windows Start manuell starten zu müssen, werden wir als nächstes den Autostart der Dienste konfigurieren. Da ich keine einfache Möglichkeit gefunden habe, das Ganze über die WSL selbst zu regeln, benötigen wir eine Batch Datei, die im Autostart Ordner von Windows verknüpft wird. Dazu ist zunächst eine Vorarbeit nötig, da sich Dienste in Linux nur mit root-Rechten und damit <code>sudo</code> starten lassen.</p>

<p>Da der Autostart möglichst unbemerkt im Hintergrund passieren soll, und daher eine Passwortabfrage für den <code>sudo</code> Befehl eher negativ auffallen würde, werden wir an dieser Stelle unserem Benutzer erlauben, alle Befehle per <code>sudo</code> ohne Passwortabfrage auszuführen. Für eure Konfiguration könnt ihr natürlich auch nur die nötigen Dienste angeben, um etwas mehr Sicherheit zu haben.</p>

<p>Öffnet eure Debian Konsole und führt folgenden Befehl aus:</p>

<pre><code class="bash">sudo visudo
</code></pre>

<p>Nach der Passworteingabe öffnet sich die Datei <code>/etc/sudoers</code> im eingestellten Standardeditor (normalerweise Nano). Sucht dort nach der Zeile <code>%sudo   ALL=(ALL:ALL) ALL</code>. Direkt darunter fügt folgendes ein:</p>

<pre><code>username ALL=(ALL) NOPASSWD:ALL
</code></pre>

<p>Ersetzt dabei <code>username</code> durch den Namen, den ihr bei der Einrichtung von Debian vergeben habt. Wenn ihr nur bestimmte Kommandos erlauben wollt, dann ersetzt das <code>ALL</code> am Ende mit dem absoluten Pfad zur ausführbaren Datei. Hier ein Beispiel, der nur den Start des Apache Dienstes erlaubt:</p>

<pre><code>username ALL=(ALL) NOPASSWD:/usr/sbin/service apache2 start
</code></pre>

<p>Nachdem die Änderungen gespeichert wurden, sollten wir nun keine störende Passwortabfrage mehr bekommen, und wir können uns dem Autostart Skript widmen.</p>

<p>Ich habe für alle Batch Dateien, die wir im weiteren Verlauf anlegen werden einen Ordner namens <code>wsl-scripts</code> direkt auf der <code>C:\</code> Partition erstellt. Darin legen wir als nächstes eine Datei namens <code>wsl-services.bat</code> mit folgendem Inhalt an:</p>

<pre><code class="bash">wsl sudo service mysql start
wsl sudo service apache2 start
</code></pre>

<p>Über das <code>wsl</code> Kommando ist es möglich Befehle auf der Windows Kommandozeile im Windows Subsystem für Linux ausführen zu lassen.</p>

<p>Legt als nächstes eine Verknüpfung zu diesem Skript per Rechtsklick -> Verknüpfung erstellen an. Ihr könnt über die Eigenschaften der Verknüpfung auch angeben, dass die Ausführung minimiert passieren soll. Damit sieht man beim Start des Rechners nur kurz das Kommandozeilensymbol in der Taskleiste erscheinen. Nachdem die Dienste gestartet wurden, verschwindet es sofort wieder.</p>

<p>Die Verknüpfung müssen wir nun nur noch in den Autostart Ordner verschieben. Drückt dazu <code>Windows-Taste + R</code> und gebt <code>shell:startup</code> ein. Es öffnet sich der Order für Autostart Programme, in den ihr nun die Verknüpfung verschieben könnt.</p>

<h6>Startverzeichnis</h6>

<p>Nachdem nun WSL2, das Windows Terminal und die Dienste darin installiert sind, und diese auch automatisch starten, fehlt noch ein letzter Schritt. Wir können das Startverzeichnis der Debian Konsole auf <code>/var/www/html</code> legen, um direkt darin losarbeiten zu können.</p>

<p>Dazu öffnet ihr erneut die Einstellungen des Windows Terminals per Aktion oder über <code>Strg + ,</code> und fügt im Definitionsteil der Debian Konsole eine weitere Zeile hinzu:</p>

<pre><code class="json">&#123;
  "guid": "&#123;07b52e3e-de2c-5db4-bd2d-ba144ed6c273&#125;",
  "hidden": false,
  "name": "Debian",
  "source": "Windows.Terminal.Wsl",
  "startingDirectory" : "//wsl$/Debian/var/www/html"
&#125;
</code></pre>

<p>Die entscheidende Definition ist <code>startingDirectory</code>. <code>//wsl$/Debian</code> definiert dabei, dass ihr innerhalb der WSL das danach folgende Verzeichnis als Standard verwenden wollt. Ihr könntet z.B. auch ein Verzeichnis eures Windows Systems angeben. Dazu einfach wie üblich den vollen Pfad angeben:</p>

<pre><code class="json">"startingDirectory": "C:/Users/User/Documents/MyFolder"
</code></pre>

<p>Damit wären wir mit dem ersten Teil der Einrichtung am Ende. Das Grundsystem läuft nun, und ihr könntet mit der Entwicklung euerer Webprojekte beginnen. Die nächsten Abschnitte beschäftigen sich mit der Installation und Einrichtung weitere Werkzeuge und Programme, um den Ablauf weiter zu vereinfachen.</p>

<h5>XServer für Windows</h5>

<p>Es gibt verschiedene XServer für Windows. Nach kurzer Suche, habe ich mich aber für die kostenpflichtige App <a href="https://x410.dev/"><strong>X410</strong></a> aus dem Microsoft Store entschieden. Solltet ihr keine Lust haben, Geld für eine Spielerei auszugeben, habe ich die Einrichtung auch mit dem Open Source Programm <a href="https://sourceforge.net/projects/vcxsrv/">VcXsrv</a> geschafft. Im wesentlichen sind die Einstellungen identisch und auch der Befehl um den XServer in Linux bekannt zu machen, ist identisch.</p>

<p>Für welches Programm ihr euch auch entschieden habt, wichtig ist, den öffentlichen Zugriff zu aktivieren. Hier der entsprechende Einstellungspunkt für X410. Klickt dazu mit der rechten Maustaste auf das Icon in der Taskleiste:</p>

<p><img src="https://next-direction.de/images/uploads/xserver-access.png" alt="X410 Zugriff erlauben" /></p>

<p>Für VcXsrv müsst ihr einfach während des Einrichtungsassistenten die Einstellung wählen, Zugriffsbeschränkungen zu deaktivieren. In beiden Fällen müsst ihr die Firewall-Abfrage bestätigen, und somit den Zugriff erlauben. Alternativ wäre es auch möglich, bestimmte Firewall-Regeln zu konfigurieren. Darauf gehe ich aber an dieser Stelle nicht ein. Wenn ihr diesen Weg gehen wollt, müsst ihr den TCP Port 6000 in Windows eingehend erlauben.</p>

<p>Neben dieser notwendigen Einstellung, könnt ihr den Rest nach belieben selbst konfigurieren. Dazu zählen Einstellungen für Vollbild- oder Fenstermodus, HighDPI oder aber die Verwendung der Zwischenablage.</p>

<h6>XServer exportieren</h6>

<p>Um Linux nun mitzuteilen, wo es den Server findet, und damit den Start von Anwendungen mit grafischer Oberfläche von der Kommandozeile zu erlauben, müsst ihr die folgenden zwei Zeilen in die Datei <code>~/.bashrc</code> einfügen:</p>

<pre><code class="bash">export DISPLAY=$(awk '/nameserver / &#123;print $2; exit&#125;' /etc/resolv.conf 2&gt;/dev/null):0.0
export LIBGL_ALWAYS_INDIRECT=1
</code></pre>

<p>Es ist zwar egal, wo ihr die Zeilen einfügt, aber ich bevorzuge immer das Ende der Datei und lasse den Rest unangetastet. Diese Zeilen setzen die nötigen Umgebungsvariablen jedes Mal, wenn ihr eine Konsole startet. Sollte es bei euch später beim Starten von Anwendungen Probleme geben, könnt ihr auch versuchen, den Wert <code>LIBGL_ALWAYS_INDIRECT</code> auf 0 zu setzen.</p>

<h6>Autostart</h6>

<p>Um X410 nicht bei jedem Windowsstart neu ausführen zu müssen, bietet es sich an, eine Verknüpfung im Autostart Ordner anzulegen. Leider bietet X410 keine Möglichkeit das über die Einstellungen zu machen. Das soll uns aber nicht davon abhalten, unsere eigene Lösung dafür zu finden.</p>

<p>Öffnet dazu mit <code>Windows-Taste + R</code> und <code>shell:startup</code> wieder den Autostart Ordner. Als nächstes öffnet ihr das Startmenü, sucht X410 in der Liste der installierten Programme, und zieht es einfach in den Ordner. Dadurch erstellt Windows die notwendige Verknüpfung.</p>

<h5>GitKraken installieren</h5>

<p>Als erste Anwendung habe ich mir für diesen Beitrag <strong>GitKraken</strong> ausgesucht. Nach mehrjähriger Verwendung bin ich sehr zufrieden damit und mit jedem Update kommen weitere nützliche Funktionen hinzu. Für den privaten Gebrauch und öffentliche GitHub Repositories ist die Verwendung kostenlos.</p>

<p>Wenn ich Pakete von Drittanbietern herunterlade und installiere, mache ich das immer im Heimatverzeichnis, welches ihr über <code>cd ~</code> erreicht. Wechselt also dorthin und ladet mit <code>wget</code> die aktuellste Version von der <a href="https://www.gitkraken.com/download">offiziellen Seite</a>:</p>

<pre><code class="bash">wget https://release.gitkraken.com/linux/gitkraken-amd64.deb
</code></pre>

<p>Im Anschluss könnt ihr das Paket über den folgenden Befehl installieren:</p>

<pre><code class="bash">sudo dpkg -i gitkraken-amd64.deb
</code></pre>

<p>Da nicht alle nötigen Abhängigkeiten automatisch aufgelöst werden können, müsst ihr im Anschluss die Installation dieser Pakete erzwingen:</p>

<pre><code class="bash">sudo apt update
sudo apt upgrade --fix-broken
</code></pre>

<p>Solltet ihr es bis zu diesem Punkt geschafft haben, könnt ihr nun über den Befehl <code>gitkraken</code> die grafische Oberfläche über den registrierten XServer in Windows starten!</p>

<p>Wenn ihr wie ich einen UHD Monitor habt, dann ist alles etwas klein. Das lösen wir in wenigen Augenblicken direkt über das Skript zum Starten der Anwendung über eine Verknüpfung in Windows. Dort können wir wieder zwei Umgebungsvariablen verwenden, die die Skalierung der Anwendung korrigiert. Ich empfehle diesen Weg, und nicht die integrierte Skalierung von X410 bzw. VcXsrv zu verwenden, da damit nach meiner Erfahrung alles sehr verschwommen dargestellt wird.</p>

<h6>Start über Verknüpfung</h6>

<p>Leider ist es doch etwas aufwändiger, eine Verknüpfung für GitKraken so anzulegen, dass es die Anwendung wie gewohnt startet. Für die folgenden Schritte orientiere ich mich an der <a href="https://x410.dev/cookbook/wsl/creating-shortcut-for-wsl-gui-desktop/">Anleitung von X410</a>. Grundsätzlich funktioniert das aber auch für VcXsrv.</p>

<p>Wechselt nun zunächst in Windows wieder in den Ordner <code>C:\wsl-scripts</code>, den wir in einem vorherigen Schritt bereits angelegt haben.</p>

<p>Erstellt in diesem Ordner eine Datei namens <code>bat-launcher.vbs</code> mit folgendem Inhalt:</p>

<pre><code>If WScript.Arguments.Count &lt;= 0 Then
    WScript.Quit
End If  

bat = Left(WScript.ScriptFullName, InStrRev(WScript.ScriptFullName, "\")) &amp; WScript.Arguments(0) &amp; ".bat"
arg = ""

If WScript.Arguments.Count &gt; 1 Then
    arg = WScript.Arguments(1)
End If

CreateObject("WScript.Shell").Run """" &amp; bat &amp; """ """ &amp; arg &amp; """", 0, False
</code></pre>

<p>Wir nehmen diesen Umweg, damit wir darüber beliebige Verknüpfungen zu unseren Anwendungen machen können, und beim Starten kein Zwischenfenster in Form einer Konsole geöffnet werden muss.</p>

<p>Legt als nächstes eine Datei namens <code>start-gitkraken.bat</code> mit folgendem Inhalt an:</p>

<pre><code>REM ### Start Linux GUI desktop
debian.exe run "export DISPLAY=$(awk '/nameserver / &#123;print $2; exit&#125;' /etc/resolv.conf 2&gt;/dev/null):0; export LIBGL_ALWAYS_INDIRECT=1; gitkraken"
</code></pre>

<p>Solltet ihr einen UHD euer Eigen nennen, fügt vor dem <code>gitkraken</code> Aufruf noch folgende Exports hinzu, um die Skalierung der Anwendung zu korrigieren:</p>

<pre><code>export GDK_SCALE=2; export GDK_DPI_SCALE=0.75;
</code></pre>

<p>Je nach Geschmack könnt ihr den zweiten Wert ändern, um die Oberfläche stärker oder schwächer zu vergrößern. Mit den gezeigten Einstellungen erreicht ihr eine Skalierung, die 150% in Windows entspricht.</p>

<p>Der letzte Schritt besteht nun darin, eine Verknüpfung anzulegen, die das obige VBScript mit der Batch Datei verbindet. Klickt dazu im aktuellen Windows Ordner mit der rechten Maustaste auf eine freie Stelle und wählt &#8220;Neu > Verknüpfung&#8221;. Es öffnet sich ein Assistent, in dem ihr zwei Angaben machen müsst.</p>

<p>Das wichtigste ist natürlich der Speicherort. Dort gebt ihr folgendes an:</p>

<pre><code>C:\Windows\System32\wscript.exe "C:\wsl-scripts\bat-launcher.vbs" "start-gitkraken"
</code></pre>

<p><code>wscript</code> wird verwendet, um das angegebene VBScript auszuführen. Dieses erhält wiederum als Parameter den Namen unserer Batch Datei, jedoch ohne Dateiendung. Diese wird während der Ausführung des Skripts angehängt.</p>

<p>Wenn ihr mit dem Assistenten fortfahrt, müsst ihr nur noch einen Namen für die Verknüpfung vergeben. In meinem Fall habe ich das Ganze passenderweise &#8220;GitKraken&#8221; genannt.</p>

<p>Und somit schließt sich der Kreis. Klickt ihr nun die Verknüpfung an, sollte GitKraken in der richtigen Skalierung gestartet werden. Per Rechtsklick auf die Verknüfpung könnt ihr sie auch noch zum Start hinzufügen und damit schnell über das Startmenü aufrufen.</p>

<h5>PHPStorm installieren und starten</h5>

<p>Zum Abschluss möchte ich noch die Schritte zeigen, um auch PHPStorm auf die gleiche Weise wie GitKraken zu starten.</p>

<dl>
<dt>Hinweis</dt>
<dd>Zum Zeitpunkt der Erstellung dieses Beitrags funktioniert die Skalierung auf UHD Monitoren über die gezeigten Umgebungsvariablen nicht. Es gibt bereits eine Meldung darüber bei <a href="https://youtrack.jetbrains.com/issue/IDEA-252794">JetBrains</a>, die ihr verfolgen könnt.</dd>
</dl>

<p>Wechselt in Linux in euer Heimatverzeichnis (<code>cd ~</code>) und ladet über folgenden Aufruf PHPStorm von der <a href="https://www.jetbrains.com/de-de/phpstorm/download/download-thanks.html?platform=linux">offiziellen Seite</a> herunter:</p>

<pre><code class="bash">wget https://download.jetbrains.com/webide/PhpStorm-2020.2.3.tar.gz
</code></pre>

<p>Beachtet, dass ihr die Version in diesem Fall an die aktuelle anpassen solltet.</p>

<p>Entpackt das Ganze mit:</p>

<pre><code class="bash">tar -xf PhpStorm-2020.2.3.tar.gz
</code></pre>

<p>Zum Testen, ob alle nötigen Pakete vorhanden sind, könnt ihr PHPStorm über folgenden Befehl von der Debian Konsole aus starten:</p>

<pre><code class="bash">PhpStorm-202.7660.42/bin/phpstorm.sh
</code></pre>

<p>Passt auch hier die Version entsprechend an.</p>

<p>Erstellt nun noch die Batch Datei im Windows Ordner <code>C:\wsl-scripts</code> mit dem Namen <code>start-php-storm.bat</code> mit folgendem Inhalt:</p>

<pre><code>REM ### Start Linux GUI desktop
debian.exe run "export DISPLAY=$(awk '/nameserver / &#123;print $2; exit&#125;' /etc/resolv.conf 2&gt;/dev/null):0; export LIBGL_ALWAYS_INDIRECT=1; /home/username/PhpStorm-202.7660.42/bin/phpstorm.sh"
</code></pre>

<p>Hier müsst ihr <code>username</code> und die Version von PHPStorm entsprechend euren Gegebenheiten anpassen.</p>

<p>Zum Schluss legt ihr die Verknüpfung wie bei GitKraken erklärt an und seit damit am Ende, sowohl mit meinen Ausführungen, als vielleicht auch mit euren Nerven 😉.</p>

<h3>Zusammenfassung</h3>

<p>Ich hoffe ich konnte euch in diesem Beitrag interessante Einblicke in WSL2 geben und euch dazu anstiften, euch selbst weiter damit zu beschäftigen. Ich finde mit Version 2 ist es ein sehr mächtiges Werkzeug geworden, dass euch in eurer Sammlung nicht fehlen sollte.</p>

<p>Jetzt will ich euch aber nicht länger aufhalten, bis zum nächsten Mal!</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Individuelle Icons als Schriftart]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/individuelle-icons-als-schriftart" />
      <id>tag:https:2019:/next-direction.de/2.19</id>
      <published>2019-03-23T17:00:00Z</published>
      <updated>2020-11-21T07:50:42Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/icons.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/icons.jpg" alt="Teaser - Individuelle Icons als Schriftart"/><br>
        <p>Willkommen zurück! Heute will ich euch eine Seite vorstellen, die es erlaubt, eure eigene Schriftart für Icons zu erstellen. Heutzutage kommt kaum eine Seite ohne Icons aus. Sei es für die Navigation oder für die Hervorhebung bestimmter Abschnitte einer Seite. Ich habe bisher immer <a href="https://fontawesome.com/">FontAwesome</a> verwendet. Hier auf Next Direction habe ich auch zuerst damit begonnen. Als ich aber die Seite von Google auf Geschwindigkeit untersuchen ließ, stellte sich heraus, dass die Schrift, die von FontAwesome geladen wird, sehr groß ist und das Laden verzögert. Das hat mich schon sehr gestört, noch dazu verwende ich von den über 600 Icons nur um die zehn auf der Seite.</p>

<p>Ich habe mich also nach Alternativen umgesehen und bin dabei auf <a href="http://fontello.com/">Fontello</a> gestoßen. Das ist ein Open Source Dienst, der es euch erlaubt, aus mehreren Icon Sets die nötigen Icons zu wählen, und dann die CSS Definitionen und die nötigen Schriftarten erzeugen zu lassen. Das erlaubt sehr kleine Dateien, die nur ein paar Kilobyte groß sind. Ich habe Next Direction mittlerweile auch auf die Dateien dieses Dienstes umgestellt. Damit konnte ich die Größe der nötigen Dateien von über 100 Kilobyte auf unter zehn reduzieren.</p>

<h3>Manuelle Erstellung</h3>

<p>Die Verwendung über die Webseite von Fontello ist dabei sehr intuitiv. Ihr wählt einfach die Icons, die ihr haben wollt, und drückt anschließend auf <em>Download webfont</em>. Das Ergebnis ist ein Archiv, in dem ihr mehrere CSS Dateien und Schriftarten in entsprechenden Ordnern findet. In der Regel benötigt ihr nur die <code>fontello.css</code> Datei und alle Schriftarten im Ordner <code>font</code>. Je nach Browser, wird automatisch die unterstütze Schriftart geladen. Da die Pfade in der CSS Datei relativ angegeben sind, müsst ihr die Dateien entsprechend in eurem Webverzeichnis ablegen.</p>

<pre><code class="markdown">webroot
 |- css/
 |---- fontello.css
 |- font/
 |---- fontello.woff2
 |- index.html
</code></pre>

<p>Die Icons könnt ihr am einfachsten über ein <code>&lt;i class="icon-mail"&gt;</code> Tag verwenden. Dabei ist das Prefix <code>icon-</code> von Fontello vorgegeben. Die Namen der einzelnen Icons werden zwar auch automatisch erzeugt, ihr könnt diese aber bei Bedarf auch anpassen, bevor ihr den Download der Dateien durchführt.</p>

<h3>Automatisierte Erstellung</h3>

<p>Auf der <a href="https://github.com/fontello/fontello">Github Seite</a> von Fontello findet ihr verschiedene Wege, wie ihr die nötigen Dateien über die Kommandozeile erstellen lassen könnt. Das ist die Grundlage, um in einem automatisierten Buildprozess aus einer Konfiguration sowohl CSS Dateien als auch Schriftarten generieren zu lassen.</p>

<p>Nachdem ihr über die Webseite eure Icons ausgewählt habt, könnt ihr neben dem Download auch nur die Konfiguration herunterladen. Dazu klickt ihr nicht direkt auf Download sondern den kleinen Pfeil rechts daneben.</p>

<p>Ich habe mich für diesen Beitrag dazu entschieden, das Node.js Kommandozeilenprogramm zu verwenden. Das installiert ihr über folgenden Befehl auf der Kommandozeile:</p>

<pre><code class="bash">npm install -g fontello-cli
</code></pre>

<p>Natürlich müsst ihr dazu Node.js und NPM auf eurem Rechner installiert haben.</p>

<p>Für den ersten Test, wechselt ihr auf der Kommandozeile nun in den Ordner, in dem sich die heruntergeladene Konfiguration befindet. Ich gehe davon aus, die Datei heißt <code>config.json</code>. Ihr könnt diese Datei natürlich auch umbenennen. Anschließend führt ihr folgendes Kommando aus und gebt eure Konfiguration an:</p>

<pre><code class="bash">fontello-cli install --config config.json
</code></pre>

<p>In dem aktuellen Verzeichnis wird daraufhin ein Ordner <code>fontello-...</code> angelegt. Darin findet ihr die gleichen Dateien, die ihr auch beim manuellen Download gesehen habt. Wenn ihr die Dateien in anderen Ordnern ablegen wollt, gibt es dafür die Optionen <code>--css</code> und <code>--font</code>, mit denen ihr die jeweiligen Ordner angeben könnt.</p>

<h3>Zusammenfassung</h3>

<p>Der Dienst, den ich euch hier vorgestellt habe, eignet sich nicht nur, um kleinere Dateien zu erzeugen, sondern auch zur Integration in euren automatisierten Buildprozess. Ihr könnt die Konfiguration dazu einfach in euer Repository aufnehmen und zum Beispiel mit einem Node Kommando die nötigen Dateien holen.</p>

<p>Wenn ihr also euer nächstes Projekt startet, denkt an Fontello. Es hilft euch, nur die Icons in eure Seite einzubinden, die ihr auch benötigt, und damit die Ladezeiten eurer Seite zu beschleunigen.</p>

<p>Je nach Umgebung eures nächsten Projekts könnt ihr euch auch meinen Beitrag zu einem <a href="https://next-direction.de/posts/full-stack-javascript-framework">Full Stack JavaScript</a> Frameworks durchlesen. Dort stelle ich euch eine Möglichkeit vor, wie ihr einzelne Icons von FontAwesome 5 über Tree Shaking verwenden könnt.</p>

<p>Jetzt will ich euch aber nicht länger aufhalten! Viel Spaß beim ausprobieren.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[In eigener Sache: Kontaktformular]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/in-eigener-sache-kontaktformular" />
      <id>tag:https:2019:/next-direction.de/2.18</id>
      <published>2019-03-17T08:00:00Z</published>
      <updated>2019-03-30T16:27:05Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/move.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/move.jpeg" alt="Teaser - Serverumzug"/><br>
        <p>Willkommen zurück! Ich melde mich etwas früher als eigentlich gedacht. Grund ist, dass der E-Mail Versand des Kontaktforumlars gestört war. Das Ganze war wohl seit 27.01.2019 so, als ich das System auf einen neuen Server umgezogen habe. Da ich zum Versand einen eigenen Mailserver verwende und dieser nur bekannte Absenderadressen erlaubt, musste ich für den Systembenutzer &#8220;www-data&#8221; auf dem Server noch eine entsprechende Adresse konfigurieren. Hier hat der Spoofingschutz also dafür gesorgt, dass eure Nachrichten nicht ankamen. Ich möchte mich nochmal für die Unannehmlichkeiten entschuldigen, die euch unter Umständen dadurch entstanden sind.</p>

<p>Das Ganze ist nun behoben und ich würde euch bitten, solltet ihr im Zeitraum von 27.01.2019 - 17.03.2019 versucht haben mich zu kontaktieren, es einfach nochmal zu versuchen. Sollte in Zukunft ein ähnliches Problem auftreten und ich auf euren Kontaktversuch nicht reagieren, könnt ihr auch gerne dem Slack Channel von Next Direction beitreten. Den Einladungslink dazu findet ihr im Fußbereich.</p>

<p>Jetzt will ich euch aber nicht länger aufhalten!</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[PHP-Framework: Routing Teil 2]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/php-framework-routing-teil-2" />
      <id>tag:https:2019:/next-direction.de/2.17</id>
      <published>2019-03-17T07:45:00Z</published>
      <updated>2019-03-17T07:48:59Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/routing2.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/routing2.jpeg" alt="Teaser - PHP-Framework: Routing Teil 2"/><br>
        <p>Willkommen zurück! Ihr lest den zweiten Teil über Routing. Solltet ihr direkt auf diesem Beitrag gelandet sein, würde ich empfehlen, vorher die anderen Beiträge dieser Serie zu lesen, da alle Beiträge mehr oder weniger aufeinander aufbauen. In diesem Fall solltet ihr auf jeden Fall vorher den ersten Teil zu Routing lesen.</p>

<p>Nachdem ihr im letzten Beitrag die Grundlagen gesehen habt, geht es heute daran, die definierten Routen mit der aufgerufenen URL abzugleichen und die zugehörige Methode des Controllers aufzurufen.</p>

<h3>Platzhalter in Routen</h3>

<p>Bevor wir aber ins Matching von Routen einsteigen, möchte ich vorab noch eine weitere Funktion vorstellen, die in den Router integriert ist. Ihr könnt in den Routen nämlich auch Platzhalter verwenden. Die benötigt ihr immer dann, wenn ihr dynamische Teile in euren URLs benötigt, z.B. um auf die Details von Beiträgen zu verlinken. In einem solchen Fall hat man in der Regel folgende URLs:</p>

<pre><code class="http">https://some-url.de/posts/1
</code></pre>

<p>Am Ende der URL seht ihr die ID des Beitrags, die natürlich vom jeweiligen Beitrag abhängig ist. Eine solche Route könnt ihr folgendermaßen definieren:</p>

<pre><code class="php">/**
 * @Route=/posts/:id
 *
 * @param Response $response
 *
 * @return Response
 */
public function detail($id, Response $response): Response &#123;
    // some logic here
&#125;
</code></pre>

<p>Ein Platzhalter in den Routen wird immer durch einen <code>:</code> eingeleitet. Für die Namensgebung des Platzhalters gelten die selben Regeln wie für Variablen in PHP. Wie ihr seht, könnt ihr den Namen des Platzhalters innerhalb der Route direkt als Name des Parameters eurer Methode angeben. Während des Dispatchings, wird der Wert aus der Route in diesem Parameter übergeben. Ohne weitere Definition des Platzhalters, solltet ihr auf Type Hinting des Parameters in der Methode vorerst verzichten.<br>
Die Reihenfolge der Methodenparameter spielt hier übrigens keine Rolle. Und wie ihr am Beispiel der <code>$response</code> Variable seht, könnt ihr die Platzhalter natürlich auch mit weiteren Parametern kombinieren, die per Dependeny Injection geliefert werden. Auch hier sei wieder auf einen späteren Beitrag dieser Serie verwiesen, in dem das Thema DI näher erläutert wird.</p>

<p>Ihr könnt so viele Platzhalter definieren, wie ihr wollt.</p>

<p>Stellen wir uns als nächstes vor, wir haben einen dynamischen Teil einer Route, und wir wissen, dieser Teil ist immer eine Ganzzahl. In diesem Fall wäre es doch praktisch, wenn wir das der Route direkt mitteilen könnten. Und ihr habt es euch wahrscheinlich schon gedacht, wenn ich diese Einleitung mache, dass das auch in meinem Framework möglich ist. Dazu schreibt ihr einfach einen beliebigen regulären Ausruck in <code>&lt;&gt;</code>. Hier das Beispiel von vorhin, um eine solche Definition erweitert:</p>

<pre><code class="php">/**
 * @Route=/posts/:id&lt;\d+&gt;
 *
 * @param Response $response
 *
 * @return Response
 */
public function detail(int $id, Response $response): Response &#123;
    // some logic here
&#125;
</code></pre>

<p>Dieser einfache reguläre Ausdruck sorgt dafür, dass die Route nur dann als Treffer erkannt wird, wenn es sich um mindestens eine Ziffer handelt. D.h. eine Route wie <code>/posts/john</code> wird nicht erkannt, während <code>/posts/1234</code> entsprechend aufgerufen wird. Jetzt könnt ihr auch den Parameter der Methode mit einem Type Hint versehen, da sichergestellt ist, dass hier immer eine Ganzzahl übergeben wird.</p>

<p>Das Ganze wird dann interessant, wenn ihr z.B. die folgenden beiden Routen habt:</p>

<pre><code class="http">https://some-url.de/posts/1
https://some-url.de/posts/create
</code></pre>

<p>In diesem Fall, soll natürlich die oben definierte Route nicht aufgerufen werden, wenn der String <code>create</code> gefunden wird. In diesem Fall wollt ihr höchstwahrscheinlich eher in einem Formular landen, mit dem ihr neue Beiträge anlegen könnt.</p>

<p>Stellen wir uns für die nächste Funktion eines Platzhalters folgende Routen vor:</p>

<pre><code class="http">https://some-url.de/posts
https://some-url.de/posts/1
</code></pre>

<p>Was, wenn ihr nun diese beiden Routen innerhalb einer Methode behandelt wollt? Auch für diesen Fall, gibt es in der Syntax für die Definition eines Platzhalter einen definierten Wert. Hier direkt das Beispiel dazu:</p>

<pre><code class="php">/**
 * @Route=/posts/:id&lt;\d+&gt;?
 *
 * @param Response $response
 *
 * @return Response
 */
public function detail(Response $response, int $id): Response &#123;
    // some logic here
&#125;
</code></pre>

<p>Ihr hängt einfach ein <code>?</code> an das Ende der Definition. Pro Route kann es nur einen optionalen Platzhalter geben und dieser muss am Ende der Route stehen. Beachtet auch, dass dieses Beispiel aktuell noch fehlschlagen würde, wenn ihr die Route <code>/posts</code> aufruft. Das liegt daran, dass als Wert für den <code>$id</code> Parameter der Methode ein <code>int</code> erwartet wird, jedoch in diesem Fall ein Leerstring übergeben wird.</p>

<p>Um dieses Problem zu lösen, gibt es zwei Wege. Ihr könnt zum einen, wie in PHP üblich, einfach einen Standardwert für euren Methodenparameter angeben:</p>

<pre><code class="php">/**
 * @Route=/posts/:id&lt;\d+&gt;?
 *
 * @param Response $response
 *
 * @return Response
 */
public function detail(Response $response, int $id = 0): Response &#123;
    // some logic here
&#125;
</code></pre>

<p>Beachtet, dass hierfür nun die Reihenfolge der Parameter getauscht werden muss, da auch PHP optionale Parameter nur am Ende der Methodendefinition erlaubt.</p>

<p>Bei der zweiten Möglichkeit, könnt ihr den Standardwert direkt in der Definition der Route angeben. Fügt den Standardwert dazu einfach hinter dem <code>?</code> ein. Hier das gleiche Beispiel wie zuvor, dieses Mal jedoch über die Routendefinition:</p>

<pre><code class="php">/**
 * @Route=/posts/:id&lt;\d+&gt;?0
 *
 * @param Response $response
 *
 * @return Response
 */
public function detail(int $id, Response $response): Response &#123;
    // some logic here
&#125;
</code></pre>

<p>Die Angabe über die Route hat den Vorteil, dass ihr den Parameter in der Methode nun wieder an beliebiger Stelle angeben könnt, da der Dispatcher sicherstellt, dass hier ein entsprechender Wert übergeben wird.</p>

<p>Das ist auch das letzte Beispiel, das ich zu Routenplatzhaltern zeigen wollte. Darin seht ihr nun eine Definition mit allen möglichen Funktionen. Hier nochmal eine Zusammenfassung:</p>

<ul>
<li>Platzhalter in Routen beginnen immer mit einem Doppelpunkt</li>
<li>Namensgebung folgt den selben Regeln wie Variablen in PHP</li>
<li>Einschränkung möglicher Werte über reguläre Ausdrücke</li>
<li>Nur ein optionaler Parameter erlaubt, dieser muss am Ende stehen</li>
<li>Standardwerte entweder per Methodenparameter oder über Routendefinition</li>
</ul>

<h3>Matching</h3>

<p>Als nächstes möchte ich euch ein paar Einblicke in das Thema Matching geben. Die komplette Funktion, zum Finden einer definierten Route, ist insgesamt 70 Zeilen lang. Ich werde daher versuchen, die wichtigen Bestandteile, in kleinen Häppchen zu erklären.</p>

<p>Die aufgerufene URL wird zunächst anhand des <code>/</code> in ihre Einzelteile zerlegt. Im Anschluss daran, werden alle definierten Routen durchlaufen, und jeder Teil mit der aktuellen Route abgeglichen:</p>

<pre><code class="php">$method = $this-&gt;request-&gt;getMethod();
$currentRoute = ltrim($this-&gt;request-&gt;getUrl(), '/');
$definedRoutes = $router-&gt;getRoutes($method);
$currentRouteParts = explode('/', $currentRoute);

foreach ($definedRoutes as $definedRoute =&gt; $handlerMethod) &#123;
    $definedRouteParts = explode('/', ltrim($definedRoute, '/'));
    $isLastPartOptional = false !== mb_strpos($definedRouteParts&#91;count($definedRouteParts) - 1&#93;, '?');

    if (!$isLastPartOptional &amp;&amp; count($definedRouteParts) !== count($currentRouteParts)) &#123;
        continue;
    &#125; else if (
        $isLastPartOptional
        &amp;&amp;  (
            count($currentRouteParts) &lt; count($definedRouteParts) - 1
            || count($currentRouteParts) &gt; count($definedRouteParts)
        )
    ) &#123;
        continue;
    &#125;

    foreach ($definedRouteParts as $index =&gt; $definedRoutePart) &#123;
        // other logic
    &#125;
&#125;
</code></pre>

<p>Ihr seht in diesem Codeausschnitt auch drei Stellen, die für gesteigerte Geschwindigkeit beim Matching sorgen sollen:</p>

<ul>
<li>Es werden nur Routen untersucht, die mit der HTTP Methode des aktuellen Aufrufs übereinstimmen</li>
<li>Routen, die keinen optionalen Platzhalter am Ende haben, werden ignoriert, wenn die Anzahl der Segmente nicht mit der aufgerufenen Route übereinstimmen</li>
<li>Routen, die einen optionalen Platzhalter haben, werden ignoriert, wenn die Anzahl der Segmente nicht übereinstimmt, wobei hier Routen gültig sind, die ein Segment länger sind, als die aktuell aufgerufene Route</li>
</ul>

<p>Innerhalb der zweiten Schleife, wird zunächst überprüft, ob es sich beim aktuell zu untersuchenden Routenabschnitt um einen statischen Teil handelt. Ist dies der Fall, aber es wird keine Übereinstimmung gefunden, wird die Route direkt ignoriert:</p>

<pre><code class="php">if (':' !== mb_substr($definedRoutePart, 0, 1)) &#123; // static part

    if ($currentRoutePart !== $definedRoutePart) &#123; // if static part not matches, no hit
        continue 2;
    &#125;
&#125;
</code></pre>

<p>Wir benötigen an dieser Stelle ein <code>continue 2</code>, da die äußere Schleife die definierten Routen durchläuft, und andernfalls nur das aktuell untersuchte Segment ignoriert werden würde.</p>

<p>Der nächste Codeausschnitt untersucht die Bestandteile eines Platzhalters:</p>

<pre><code class="php">$placeholderName = mb_substr($definedRoutePart, 1);
$regex = $defaultValue = '';

if (false !== mb_strpos($placeholderName, '&lt;')) &#123; // dynamic part has to match regex
    list($placeholderName,) = explode('&lt;', $placeholderName);

    if (preg_match('/&lt;(.*)&gt;/', $definedRoutePart, $regexMatch)) &#123;
        $regex = $regexMatch&#91;1&#93;;
    &#125;
&#125; else if (false !== mb_strpos($placeholderName, '?')) &#123; // dynamic part is optional
    list($placeholderName,) = explode('?', $placeholderName);
&#125;

if (false !== mb_strpos($definedRoutePart, '?')) &#123; // check if dynamic part has default value
    $defaultValue = explode('?', $definedRoutePart)&#91;1&#93; ? : '';
&#125;

// if dynamic part not matches regex, no hit (only if not empty, can only occur if last part is optional)
if ($regex &amp;&amp; !preg_match('/' . $regex . '/', $currentRoutePart) &amp;&amp; '' !== $currentRoutePart) &#123;
    continue 2;
&#125;
</code></pre>

<p>Zuerst wird überprüft, ob es einen regulären Ausdruck gibt. Wenn es sich um einen optionalen Platzhalter handelt, wird als nächstes überprüft, ob dieser einen Standardwert aufweist. Sollte das Routensegment nicht dem regulären Ausdruck entsprechen, oder ein optionaler Platzhalter hat keinen Wert bzw. Standardwert, wird die aktuelle Route wiederum mit <code>continue 2</code> ausgelassen.</p>

<p>Sollte eine Route alle Prüfungen überstehen, wird nichts mehr durchlaufen. Der erste Treffer wird verwendet und es werden alle ermittelten Werte, die für den Dispatcher wichtig sind, gespeichert:</p>

<pre><code class="php">$this-&gt;routeParams = $routeParameters;
$this-&gt;defaultValues = $defaultValues;
$this-&gt;matchedHandler = $handlerMethod;
$this-&gt;matchedRoute = $definedRoute;
</code></pre>

<p>Die Werte der Platzhalter und die Standardwerte werden getrennt gespeichert. Außerdem wird die Methode gespeichert, die der Route entspricht. Zuletzt wird auch die gefundene Route noch gespeichert.</p>

<h3>Dispatching</h3>

<p>Das letzte Puzzleteil ist das Dispatching. Damit ist der Schritt gemeint, der die gefundene Methode nun aufruft, und das Ergebnis zurückliefert. Ich möchte an dieser Stelle nur sehr oberflächlich auf diese Logik eingehen, da ein Großteil davon Dependency Injection darstellt, und ich dieses Thema in einem der nächsten Beiträge ausführlicher vorstellen möchte.</p>

<p>Hier der eigentliche Aufruf der Handlermethode und das Senden der Antwort an den Browser:</p>

<pre><code class="php">list($fullQualifiedClassName, $handlerMethodName) = explode('::', $matcher-&gt;getMatchedHandler());
$handler = ObjectFactory::createInstance($fullQualifiedClassName);
$arguments = &#91;&#93;;

// some other logic to get arguments

try &#123;
    $response = call_user_func_array(&#91;$handler, $handlerMethodName&#93;, $arguments);
&#125; catch (\Exception $e) &#123;
    /** @var Response $response */
    $response = ObjectFactory::createInstance(Response::class);
    $response
        -&gt;setCode(ResponseCodes::HTTP_INTERNAL_SERVER_ERROR)
        -&gt;setBody($e-&gt;getMessage());
&#125;

if ($response instanceof Response) &#123;
    $response-&gt;send();
&#125; else &#123;
    /** @var Response $responseObject */
    $responseObject = ObjectFactory::createInstance(Response::class);
    $responseObject
        -&gt;setBody((string) $response)
        -&gt;send();
&#125;
</code></pre>

<p>Die Variablen <code>$handler</code> und <code>$handlerMethodName</code> werden dabei direkt aus den Informationen erstellt bzw. ausgelesen, die der Matcher zur Verfügung stellt. Das Array mit den Argumenten, wird im Vorfeld über Analyse der aufzurufenden Methode mittels Reflection und Dependency Injection gefüllt.</p>

<p>Sollte bei dem Aufruf etwas schief laufen, wird ein entsprechender Fehler an den Browser geschickt. Wenn der Rückgabewert der aufgerufenen Methode keine Instanz eines Responses ist, wird dieser Wert als Body an ein dafür erstelltes Response Objekt übergeben, das daraufhin an den Browser gesendet wird.</p>

<h3>Zusammenfassung</h3>

<p>In diesem und dem vorhergehenden Beitrag habe ich viele Einblicke in das Routing über Annotationen gegeben. Ich hoffe, meine Ausführungen waren verständlich, und ihr habt nun ein besseres Gespür dafür, wie das Ganze funktioniert. Ihr habt auch gesehen, dass ein Framework sehr viel Arbeit im Hintergrund erledigen muss, damit ihr eure Routen so einfach wie möglich definieren könnt.</p>

<p>Nun will ich euch aber nicht länger aufhalten! Wir lesen uns beim nächsten Mal.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[PHP-Framework: Routing Teil 1]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/php-framework-routing-teil-1" />
      <id>tag:https:2019:/next-direction.de/2.16</id>
      <published>2019-03-09T10:00:00Z</published>
      <updated>2019-03-09T10:02:50Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/routing1.jpg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/routing1.jpg" alt="Teaser - PHP-Framework: Routing Teil 1"/><br>
        <p>Willkommen zurück! Im heutigen Beitrag werden wir mit dem Thema Routing beginnen. Da es sich hier um ein eher komplexes Thema handelt, das aus vielen einzelnen Komponenten besteht, habe ich mich dafür entschieden, zwei Beiträge zu erstellen. Du liest aktuell den ersten, in dem es um die grundlegenden Funktionen geht. Dazu gehören das Durchsuchen der Verzeichnisse nach Controllern und die Ermittlung definierter Routen über Reflection.</p>

<p>Solltest du direkt auf diesem Beitrag gelandet sein, empfehle ich zuerst die vorhergehenden Beiträge dieser Serie zu lesen. Alle Beiträge bauen aufeinander auf.</p>

<h3>Verzeichnisse durchsuchen</h3>

<p>Historisch gesehen, bietet PHP mehrere Möglichkeiten, um ein Verzeichnis nach bestimmten Dateien zu durchsuchen. Die folgenden Abschnitte stellen zwei der älteren und einen modernen Ansatz vor, den ich auch im Framework verwendet habe.</p>

<h5>Historisch: opendir, scandir, is_dir</h5>

<p>Die erste historische Methode benutzt die Verzeichnisfunktionen von PHP. Mit dieser Methode, müsst ihr ein Verzeichnis mit <code>opendir</code> öffnen, mit <code>scanndir</code> durchlaufen und mit <code>is_dir</code> überprüfen, ob es sich um ein Verzeichnis oder eine Datei handelt. Wenn ihr ein Verzeichnis gefunden habt, müsst ihr die gleichen Schritte solange wiederholen, bis alle Dateien aus allen Unterverzeichnissen gefunden wurden.</p>

<p>Obwohl diese Methode natürlich funktioniert, muss doch sehr Vieles manuell erledigt werden. Ich müsst z.B. sicherstellen, dass die Verzeichnisse <code>.</code> und <code>..</code> nicht gescannt werden, sonst könnt ihr schnell in einer Endlosschleife landen. Ein weiterer Faktor, der für mich entscheidend war, ist die Tatsache, dass der komplette Code im PHP 4 Stil, also prozedural, entwickelt werden müsste. Auch wenn die Logik in eigene Klassen verpackt wird, ist es doch der alte Stil.</p>

<p>Falls euch die nötige Logik interessiert, findet ihr eine ausprogrammierte Funktion auf <a href="http://php.net/manual/de/function.scandir.php#110570">php.net</a>. Je nach Anwendungsfall kann es nötig sein, die Funktion an eure Bedürfnisse anzupassen.</p>

<h5>Historisch: glob</h5>

<p>Auch die <code>glob</code> Funktion eignet sich dazu, Dateien innerhalb einer Verzeichnisstruktur zu suchen. Laut Dokumentation, kann es hier Probleme geben, wenn ihr wirklich viele Dateien durchsuchen wollt (> 100 000). Eine weitere Einschränkung ist, dass es eher umständlich ist, damit auch Unterverzeichnisse zu durchsuchen.</p>

<p>Eine mögliche Lösung für das Problem der Unterverzeichnisse findet ihr z.B. auf <a href="https://stackoverflow.com/a/17161106">Stack Overflow</a>. Das Problem mit zu vielen Dateien lässt sich nur lösen, indem ihr PHP mehr Speicher über die php.ini gebt, da <code>glob</code> alle gefundenen Dateien in einem großen Array speichert.</p>

<h5>Modern: Iteratoren</h5>

<p>Ihr seht also, die beiden zuvor vorgestellten Methoden haben verschiedene Fallstricke. Kommen wir deshalb zur modernen Methode, die seit PHP 5 direkt in die Sprache integriert ist. Dabei handelt es sich um Iteratoren. Diese durchsuchen Verzeichnisse, wenn gewünscht auch rekursiv (also inklusive Unterverzeichnisse), und können bei Bedarf reguläre Ausdrücke auf die gefundenen Dateien anwenden. Im Gegensatz zu <code>glob</code>, werden die Treffer einzeln zurückgegeben, wodurch sich hier kein Speicherproblem ergibt. All das werde ich euch in kürze in einem Beispiel genauer zeigen.</p>

<p>Bei Anwendungen, die auf Basis des hier vorgestellten Frameworks entwickelt werden, gibt es in der Anwendungskonfiguration <code>config/app.php</code> einen Wert, der den Pfad zu bestimmten Klassen enthält. Für das hier beschriebene Routing, sind die Controller interessant. Der Pfad dafür ist im Wert <code>controllerDirectory</code> zu finden.</p>

<p>Da das Durchsuchen von Verzeichnissen und Finden von bestimmten Klassen eine häufige Aufgabe innerhalb des Frameworks darstellt, wurde die Logik in eine Hilfsklasse ausgelagert. Die Klasse heißt <code>DirectoryInspection</code> und erledigt zwei Dinge. Die erste Funktion, durchsucht ein übergebenes Verzeichnis rekursiv nach PHP Dateien. Die zweite Funktion inspiziert diese Dateien und gibt als Ergebnis die voll qualifizierten Klassennamen zurück. Voll qualifiziert bedeutet, dass auch der Namespace enthalten ist. Das ist wichtig, um daraus, unter Zuhilfenahme von Autoloading, direkt eine Instanz erstellen oder wie später beschrieben, Reflection verwenden zu können.</p>

<p><strong>Verzeichnis durchsuchen</strong></p>

<p>Werfen wir also zunächst einen Blick auf die Funktion, die alle PHP Dateien sucht:</p>

<pre><code class="php">/**
 * Return file name and path to all php files in given directory
 *
 * @param string $directory
 *
 * @return array
 */
public static function getFiles(string $directory): array &#123;
    $directoryIterator = new \RecursiveDirectoryIterator($directory);
    $iterator = new \RecursiveIteratorIterator($directoryIterator);
    $fileIterator = new \RegexIterator($iterator, '/.+\.php$/i', \RecursiveRegexIterator::MATCH);
    $files = &#91;&#93;;

    /** @var \SplFileInfo $file */
    foreach ($fileIterator as $file) &#123;
        $files&#91;&#93; = $file-&gt;getPathname();
    &#125;

    return $files;
&#125;
</code></pre>

<p>Ich finde, der Code ist sehr übersichtlich, wenn ihr bedenkt, was hier alles passiert. Werfen wir also einen Blick auf die einzelnen Iteratoren und was deren Aufgabe ist.<br> 
Da hätten wir als erstes den <code>RecursiveDirectoryIterator</code>. Dieser ist einzig und alleine dafür zuständig, rekursiv Verzeichnisse zu lesen.<br>
Als nächstes haben wir den <code>RecursiveIteratorIterator</code>. Er dient dazu, die einzelnen Verzeichnis Iteratoren, die uns der <code>RecursiveDirectoryIterator</code> liefert, zu durchlaufen.<br>
Als letztes packen wir die Iteratoren für die Dateien, die wir vom vorherigen Aufruf erhalten, noch in den <code>RegexIterator</code>. Dieser wendet dann einen regulären Ausdruck auf alle gefundenen Dateien an. In unserem Fall, sollen alle Dateien mit der Endung <code>.php</code> gesucht werden. Der Parameter <code>RecursiveRegexIterator::MATCH</code> sorgt dafür, dass lediglich der Filter angewendet wird, sonst aber keine weitere Aktion ausgeführt wird. Wer genauere Informationen zu den verschiedenen Modi dieses Iterators haben will, kann sich dazu die Dokumentation auf <a href="http://php.net/manual/de/class.regexiterator.php#regexiterator.constants.operation-modes">php.net</a> anschauen.</p>

<p>Das Ergebnis all dieser Aufrufe ist ein Iterator, den wir einfach mit <code>foreach</code> durchlaufen können, und der die Dateien in einer <code>SplFileInfo</code> Klasse verpackt ausspuckt. Daraus können wir den vollen Pfad zur Datei ermitteln, den wir im weiteren Verlauf benötigen.</p>

<p>Ich muss gestehen, für mich ist diese Verschachtelung auch jedes Mal eine neue Herausforderung. Aber die Einarbeitung in diese Klassen lohnt sich, wenn ihr häufig mit der Aufgabe der Verzeichnis- bzw. Dateisuche zu tun habt.</p>

<h3>Klassen ermitteln</h3>

<p>Wir haben nun also eine Liste mit Pfaden zu Dateien, die mögliche Controller Klassen und damit Routen als Annotationen beinhalten können. Um damit was anfangen zu können, benötigen wir als nächstes die Namen der Klassen, am besten mit Namespaces, was auch als voll qualifizierter Klassenname bezeichnet wird.</p>

<p>Nach kurzer Suche, was es hier für Möglichkeiten gibt, bin ich auf die Funktion <code>token_get_all()</code> gestoßen. Diese Funktion erwartet den PHP Quellcode als String und gibt ein Array zurück, in dem alle PHP Tokens dieses Codes zu finden sind. Um zu verdeutlichen, was diese Funktion zurückgibt, sehen wir uns ein kleines Beispiel aus der Dokumentation an:</p>

<pre><code class="php">$tokens = token_get_all('&lt;?php echo; ?&gt;');

foreach ($tokens as $token) &#123;

    if (is_array($token)) &#123;
        echo "Line &#123;$token&#91;2&#93;&#125;: ", token_name($token&#91;0&#93;), " ('&#123;$token&#91;1&#93;&#125;')", PHP_EOL;
    &#125;
&#125;
</code></pre>

<p>Das erste was auffällt ist, dass es wohl innerhalb des Arrays noch Unterarrays geben kann. Diese enthalten dann weitere Informationen zum Token:</p>

<ul>
<li>Tokentyp: Eine Konstante, deren Namen wir über die Funktion <code>token_name</code> erhalten</li>
<li>Tokenwert: Hier finden wir z.B. den Klassennamen oder einen Teil des Namespaces</li>
<li>Zeilennummer: Normalerweise zu vernachlässigen</li>
</ul>

<p>Soweit ich gesehen habe, ist <code>;</code> der einzige Wert, der nicht als Array geliefert wird. Trotzdem ist es ein sehr wichtiger Wert, um z.B. bestimmen zu können, wann ein Namespace vollständig ist.</p>

<p>Hier die Ausgabe des obigen Codes:</p>

<pre><code class="ini">Line 1: T_OPEN_TAG ('&lt;?php ')
Line 1: T_ECHO ('echo')
Line 1: T_WHITESPACE (' ')
Line 1: T_CLOSE_TAG ('?&gt;')
</code></pre>

<p>Ihr könnt sicher bereits erkennen, dass dieses Array nicht ganz so einfach zu verarbeiten ist. Dazu kommt noch die Tatsache, dass die einzelnen Bestandteile eines Namespaces als einzelne Tokens enthalten sind.</p>

<p>Die komplette Funktion, die ich zur Analyse dieses Arrays verwende, um Namespace und Klassenname zu ermitteln, umfasst ca. 55 Zeilen. Da die Funktion etwas zu lang ist, um hier den kompletten Quellcode zu zeigen, möchte ich kurz die darin befindliche Logik beschreiben.</p>

<p><strong>Ermittlung des Namespaces</strong></p>

<p>Um den Namespace zu erhalten, suche ich zunächst nach dem Token mit dem Wert <code>namespace</code>. Ab diesem Zeitpunkt wird alles, was nicht einem Leerzeichen entspricht, mittels <code>\</code> zusammengefügt, bis ich das Token mit dem Wert <code>;</code> finde. Danach ist der Namespace komplett.</p>

<p><strong>Ermittlung des Klassennamens</strong></p>

<p>Die Ermittlung des Klassennamens ist nicht ganz so aufwändig. Hier warte ich auf das Token <code>class</code> und nehme den nächsten Wert, der kein Leerzeichen ist.</p>

<p><strong>Voll qualifizierter Klassenname</strong></p>

<p>Der einfachste Teil ist dann, die beiden vorhergehenden Bestandteile, mit einem <code>\</code> dazwischen, zusammenzufügen.</p>

<h3>Einführung Annotationen</h3>

<p>Ich habe ja bereits mehrfach erwähnt, dass ich das Routing in meinem Framework per Annotationen machen möchte. Das ist eine Definition innerhalb des PHPDoc Kommentarblocks einer Klasse oder einer Methode dieser Klasse.</p>

<p>Für das Routing in meinem Framework gibt es aktuell drei mögliche Definitionen:</p>

<ul>
<li><strong>@RoutePrefix</strong> - Definiert ein Prefix für alle Routen eines Controllers und wird deshalb direkt für die Klasse definiert.</li>
<li><strong>@Route</strong> - Definiert eine einzelne Route und wird auf einer Methode definiert.</li>
<li><strong>@Method</strong> - Definiert die HTTP Methode, mit der eine Route aufgerufen werden kann (Standard: GET). Mögliche Methoden befinden sich im Enum <code>RequestMethods</code> des <code>Http</code> Namespaces.</li>
</ul>

<p>Hier ein einfaches Beispiel für einen User Controller:</p>

<pre><code class="php">/**
 * @RoutePrefix=/user
 *
 * @package NextDirection\Application\Controller
 */
class User &#123;

    /**
     * @Route=/
     * @Method=GET
     *
     * @param Response $response
     *
     * @return Response
     */
    public function index(Response $response): Response &#123;        
        return $response;
    &#125;
&#125;
</code></pre>

<p>Obwohl die GET Methode als Standard definiert ist und somit auch weggelassen werden kann, wollte ich doch alle möglichen Definitionen in diesem Beispiel zeigen. Beschäftigen wir uns nun als nächstes damit, wie wir diese Information aus diesen Definitionen extrahieren können, um Routing zu ermöglichen.</p>

<h3>Reflektieren gefundener Klassen</h3>

<p>Nachdem wir nun alle Klassen innerhalb des Controller Verzeichnisses identifiziert haben, können wir sie genauer untersuchen. Dazu verwenden wir die in PHP integrierte <a href="https://secure.php.net/manual/de/class.reflectionclass.php">ReflectionClass</a> Klasse. Diese erwartet im Konstruktor als einzigen Parameter den voll qualifizierten Namen der Klasse, die untersucht werden soll:</p>

<pre><code class="php">$reflectionClass = new \ReflectionClass($className);
</code></pre>

<p>Über die Instanz der Klasse können wir nun Informationen abfragen, wie die Methoden oder den PHPDoc Kommentar. Leider werden die Bestandteile des Kommentars nicht über die integrierten Funktionen zerlegt. Hier müssen wir also selbst Hand anlegen.</p>

<p><strong>Prefix prüfen</strong></p>

<p>Um das Prefix aller nachfolgenden Routen zu ermitteln, verwende ich folgende Logik:</p>

<pre><code class="php">$prefixRegEx = '/@RoutePrefix=(.*)\s/m';
$prefix = preg_match($prefixRegEx, $reflectionClass-&gt;getDocComment(), $prefixMatch) ? $prefixMatch&#91;1&#93; : '';
</code></pre>

<p>Die Funktion <code>preg_match</code> gibt praktischerweise einen Boolean mit den Werten <em>wahr</em> oder <em>falsch</em> zurück. Deshalb kann innerhalb einer Zeile direkt das gefundene Prefix oder ein Leerstring ermittelt werden. Der verwendete reguläre Ausdruck sucht dabei alles, was nach dem <code>=</code> kommt und kein sog. Whitespace Zeichen ist. Das sind z.B. Leerzeichen, Zeilenumbrüche oder Tabulatoren. Wer selbst oft mit regulären Ausdrücken zu tun hat, dem kann ich nur <a href="https://regex101.com/#pcre">folgende Seite</a> empfehlen. Damit könnt ihr sehr einfach eure Ausdrücke zusammenstellen und auch gleich prüfen, was sie finden.</p>

<p><strong>Route und Methode bestimmen</strong></p>

<p>Hier will ich nicht zu viele Worte verlieren, sondern direkt den Beispielcode zeigen, den ich danach etwas genauer erklären werde:</p>

<pre><code class="php">$routeRegEx = '/@Route=(.*)\s/m';
$methodRegEx = '/@Method=(.*)\s/m';

foreach ($reflectionClass-&gt;getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) &#123;
    $docComment = $reflectionMethod-&gt;getDocComment();
    $method = preg_match($methodRegEx, $docComment, $methodMatch) ? $methodMatch&#91;1&#93; : 'GET';
    preg_match($routeRegEx, $docComment, $routeMatch);

    $route = $routeMatch ? $routeMatch&#91;1&#93; : null;

    if (null !== $route) &#123;

        if (!isset($this-&gt;routes&#91;$method&#93;)) &#123;
            $this-&gt;routes&#91;$method&#93; = &#91;&#93;;
        &#125;

        $fullRoute = str_replace('//', '/', $prefix . $route);
        $fullRoute = mb_strlen($fullRoute) &gt; 1 ? rtrim($fullRoute, '/') : $fullRoute;

        if ($this-&gt;checkRouteValidity($fullRoute)) &#123;
            $this-&gt;routes&#91;$method&#93;&#91;$fullRoute&#93; = $className . '::' . $reflectionMethod-&gt;getName();
        &#125;
    &#125;
&#125;
</code></pre>

<p>Innerhalb einer Schleife werden alle öffentlichen Methoden der zu untersuchenden Klasse durchlaufen. Im Wesentlichen wird für die Ermittlung von Route und Methode ein ähnliches Vorgehen wie beim Prefix angewendet. Diesmal wird der PHPDoc Kommentar von den Methoden untersucht.</p>

<p>Die gefundenen Routen werden je nach HTTP Methode gruppiert, um das Matching, welches im nächsten Beitrag beschrieben wird, zu beschleunigen. Nach Normalisierung der gefundenen Route (Entfernung überflüssiger <code>/</code> und entfernen des abschließenden <code>/</code>), wird sie noch auf Gültigkeit überprüft und dann in einem Array gespeichert.</p>

<p>Wann genau eine Route gültig ist, wird auch im nächsten Beitrag genauer beleuchtet.</p>

<h3>Zusammenfassung</h3>

<p>Ich habe euch in diesem Beitrag eine Menge an Routing Grundlagen gezeigt. Nachdem wir zuerst ein bestimmtes Verzeichnis inklusive Unterverzeichnissen nach PHP Dateien durchsucht haben, wurden diese Dateien analysiert, um die voll qualifizierten Klassennamen zu erhalten. Im Anschluss daran, wurden die Klassen genauer untersucht und definierte Routen extrahiert.</p>

<p>Im nächsten Beitrag werden wir uns ansehen, wie die Verarbeitung einer aufgerufenen Route abläuft, und wie wir dann in der entsprechenden Methode des Controllers landen.</p>

<p>Nun will ich euch aber nicht länger aufhalten! Ich hoffe ihr haltet die Spannung bis zum nächsten Beitrag aus.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[PHP-Framework: Grundlagen]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/php-framework-grundlagen" />
      <id>tag:https:2019:/next-direction.de/2.15</id>
      <published>2019-03-02T08:30:00Z</published>
      <updated>2019-03-02T08:28:48Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/framework-basics.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/framework-basics.jpeg" alt="Teaser - PHP-Framework: Grundlagen"/><br>
        <p>Willkommen zurück! Bevor wir uns ins Eingemachte stürzen und mit den komplexeren Themen beginnen, möchte ich in diesem Beitrag kurz auf die grundlegenden Funktionen eingehen, auf denen dann auch weitere Komponenten basieren. Solltest du direkt auf diesem Beitrag gelandet sein, empfehle ich zuerst die vorhergehenden Beiträge dieser Serie zu lesen. Alle Beiträge bauen aufeinander auf.</p>

<h3>Enums</h3>

<p>PHP hat zwar native <a href="http://php.net/manual/de/class.splenum.php">Aufzählungen</a> über die <a href="http://php.net/manual/de/book.spl.php">Standard-PHP-Library</a> (kurz SPL). Ich habe mich für mein Framework trotzdem dafür entschieden, eine eigene Implementierung zu verwenden. Die SPL Implementierung hat zwar den Vorteil, das man die Werte als Typ für Funktionsparameter verwenden kann, jedoch muss man für die Verwendung immer Instanzen von Klassen erstellen.</p>

<p>Meine eigene Implementierung setzt stattdessen auf statische Konstanten in einer abstrakten Klasse. Diese erbt wiederum von einer Basis Enum Klasse, die zwei hilfreiche Funktionen bereitstellt. Mit der ersten könnt ihr euch alle definierten Werte zurückgeben lassen, während die zweite dafür zuständig ist, zu überprüfen, ob ein gegebener Wert für die Aufzählung gültig ist.</p>

<p>Hier ein Beispiel für eine Aufzählung, die alle verfügbaren Konfigurationsdateien enthält:</p>

<pre><code class="php">&lt;?php
namespace NextDirection\Framework\Config;

use NextDirection\Framework\Common\EnumBase;

abstract class Types extends EnumBase &#123;

    /**
     * Application configuration
     *
     * @var string
     */
    public const APP = __DIR__ . '/../../config/app.php';

    /**
     * DI Mapping
     *
     * @var string
     */
    public const DI = __DIR__ . '/../../config/di.php';

    /**
     * File cache config
     *
     * @var string
     */
    public const FILE_CACHE = __DIR__ . '/../../config/fileCache.php';
&#125;
</code></pre>

<p>Um einen Aufzählungswert zu verwenden, könnt ihr folgende Logik verwenden:</p>

<pre><code class="php">$appConfig = \NextDirection\Framework\Config\Types::APP;
</code></pre>

<p>Um zu prüfen, ob ein an eine Funktion übergebener Wert gültig ist, dient der folgende Aufruf:</p>

<pre><code class="php">$isValid = \NextDirection\Framework\Config\Types::isValid($appConfig);
</code></pre>

<p>Im Laufe dieser Serie, werde ich immer wieder Beispiele für Enums zeigen. Dabei werde ich dann der Einfachheit halber aber nur noch die Klasse mit den Konstanten ohne jeglichen Overhead zeigen. Es werden also alle Kommentare und Namespaces ebenso wie <code>use</code> Anweisungen fehlen.</p>

<h3>Config Reader</h3>

<p>Im vorherigen Abschnitt habe ich bereits etwas vorgegriffen und euch die Pfade zu möglichen Konfigurationsdateien, in Form eines Enums, gezeigt. Dieser Abschnitt befasst sich nun mit einer kleinen Hilfsklasse, um diese Konfigurationen einzulesen und auf einzelne Werte daraus zuzugreifen.</p>

<p>Hier zuerst die relativ unspektakuläre Konfigurationsdatei:</p>

<pre><code class="php">&lt;?php
// config/app.php

return &#91;
    'controllerDirectory' =&gt; __DIR__ . '/../app/Controller',
    'modelDirectory'      =&gt; __DIR__ . '/../app/Model',
    'commandDirectory'    =&gt; __DIR__ . '/../app/Command',
    'prettyExceptions'    =&gt; false
&#93;;
</code></pre>

<p>Und hier der Code, um einen Wert dieser Konfiguration einzulesen:</p>

<pre><code class="php">$appConfig = new Reader(Types::APP);
$controllerDirectory = $appConfig-&gt;get('controllerDirectory');
</code></pre>

<p>Aus Gründen der Übersichtlichkeit, verzichte ich bei diesen Beispielen auf volle Namespaces. Bei <code>Types</code> handelt es sich um das im vorherigen Abschnitt beschriebene Enum. Der <code>Reader</code> liest die Konfiguration aus dem gegebenen Pfad und erlaubt den Zugriff per <code>get()</code>. Im Beispiel wird ein spezieller Wert abgefragt. Wenn ihr die Methode ohne einen Parameter aufruft, bekommt ihr alle Werte als Array übergeben.</p>

<h3>Umgebungsvariablen</h3>

<p>Dieser Abschnitt hängt ein bisschen mit dem vorhergehenden zusammen. Neben Konfigurationen können über eine entsprechende Hilfsklasse auch Umgebungsvariablen eingelesen werden. Ich habe mich hier bei der Implementierung an Symfony orientiert.</p>

<p>Im Hauptverzeichnis liegt eine Datei namens <code>.env.dist</code>. Diese enthält alle möglichen Umgebungsvariablen, die innerhalb des Frameworks benötigt werden. Um die Umgebungsvariablen zu aktivieren, müsst ihr diese Datei kopieren und <code>.env</code> nennen. Diese Datei ist über die <code>.gitconfig</code> vor versehentlichen Commits geschützt. Ihr könnt diese Datei natürlich beliebig erweitern, wenn ihr für die Anwendungslogik eigene Variablen benötigt. Stellt auf jeden Fall sicher, dass keine sensiblen Informationen in der Datei <code>.env.dist</code> stehen, die versehentlich in euer Repository gelangen könnten.</p>

<p>Der Zugriff auf die Variablen erfolgt vergleichbar zu Konfigurationswerten:</p>

<pre><code class="php">$env = new Environment();
$jwtSecret = $env-&gt;get('JWT_SECRET');
</code></pre>

<p>Natürlich könnt ihr die Umgebungsvariablen auch direkt per Dependency Injection in einen Controller holen. Mehr dazu in einem der nächsten Beiträge dieser Serie.</p>

<p>Zwei Punkte, die ich hierzu kurz erwähnen möchte:</p>

<ul>
<li>Konventionen sehen vor, Umgebungsvariablen mit Großbuchstaben und Unterstrichen zu schreiben</li>
<li>Im Gegensatz zu Konfigurationen, ist es hier nicht möglich die Funktion <code>get()</code> ohne Parameter aufzurufen, um alle Werte zu erhalten</li>
</ul>

<h3>Cache</h3>

<p>Je nach Komplexität einer Anwendung, kann es einen großen Unterschied machen, ob die Ergebnisse komplexer Algorithmen zwischengespeichert werden oder nicht. Für mein Framework habe ich daher eine PSR-6 konforme Implementierung eines Dateisystem-Caches integriert.</p>

<p>Da dieser Beitrag lediglich eine kurze Einführung in die Grundlagen darstellen soll, werde ich nicht zu sehr auf Details der Umsetzung eingehen. Im Wesentlichen handelt es sich um eine Implementierung, die den Standard <a href="https://www.php-fig.org/psr/psr-6/">PSR-6</a> umsetzt.</p>

<p>Hier ein paar Besonderheiten, die zu beachten sind:</p>

<ul>
<li>Die Schlüssel mit denen Werte im Cache gespeichert werden sollen, dürfen folgende Zeichen nicht enthalten: { } ( ) / \ @ :</li>
<li>Der Wert <code>null</code> kann nicht gespeichert werden, da er zur Prüfung eines Treffers verwendet wird</li>
<li>Jeder Wert wird innerhalb einer Datei gespeichert</li>
<li>Die Gültigkeitsdauer eines Wertes wird über die Änderungszeit der jeweiligen Datei geregelt</li>
<li>Der Cache kann über die Datei <code>config/fileCache.php</code> konfiguriert werden (Pfad + Standard Gültigkeit)</li>
</ul>

<h3>Request</h3>

<p>Wer schon mal eine größere PHP Anwendung entwickelt hat, kann sicher meine Erfahrungen bezüglich der Abarbeitung eines Requests teilen. Man hat mit einer Vielzahl verschiedener Probleme zu kämpfen, für die man auf unterschiedlichste globale Variablen oder Funktionen zurückgreifen muss. Vor allem bei der Entwicklung einer API treten u.a. folgende Situationen ein:</p>

<ul>
<li>Verarbeitung von Anfrage-Headern, um Content-Type oder unterstütze Sprachen auszulesen</li>
<li>Auslesen von GET oder POST Variablen</li>
<li>Zugriff auf Cookies oder Dateien</li>
</ul>

<p>Für alle diese Fälle, gibt es innerhalb des Frameworks eine Abstraktion, die einheitliche Aufrufe zum Abfragen benötigter Informationen anbietet. Das beste daran ist, dass ihr über Dependency Injection diese Abstraktion direkt in euren Controller geliefert bekommt. Mehr dazu gibt es, wie bereits erwähnt, in einem der Folgebeiträge.</p>

<p>Folgende Informationen können aus dem Request ausgelesen werden:</p>

<ul>
<li>Die aufgerufene URL</li>
<li>Die verwendete HTTP Methode</li>
<li>POST, GET, COOKIE und FILES Variablen</li>
<li>Das verwendete HTTP Protokoll und ob es sich um eine sichere Anfrage handelt (HTTPS)</li>
<li>Die Anfrage-Header</li>
<li>Den unformatierten Inhalt der Anfrage (Raw Body)</li>
</ul>

<p>Für die Verwendung der Header, gibt es einen Punkt zu beachten. Die Browserheader werden vom Webserver in der Variable <code>$_SERVER</code> in der Form <code>HTTP_ACCEPT_ENCODING</code> geliefert. Für die Verwendung in der Request Klasse, wird das Prefix <code>HTTP_</code> entfernt, und die Schreibweise zu <code>Accept-Encoding</code> geändert. Die verbleibenden Unterstriche werden dabei durch Bindestriche ersetzt und der Anfangsbuchstabe eines jeden Wortes wird groß geschrieben, der Rest des Wortes klein.</p>

<p>Die Zugriffe erfolgen sehr ähnlich zu den bereits beschriebenen Konfigurationen oder Umgebungsvariablen:</p>

<pre><code class="php">$request = Request::createInstance();
$request-&gt;getPost();
$request-&gt;getCookie('Authorization');
$request-&gt;isSecure();
$request-&gt;getHeader('Content-Type');

</code></pre>

<p>Bei der Instanzierung des Request Objekts gibt es einen Unterschied zu bisherigen Beispielen. Da es sich beim Request um ein <a href="https://de.wikipedia.org/wiki/Singleton_(Entwurfsmuster)">Singleton</a> handelt, darf die Erzeugung nicht über den Konstruktor laufen. Es wäre sonst nicht sichergestellt, dass es nur eine Instanz pro Request gibt. Da sich die darin gespeicherten Informationen während der Abarbeitung nicht ändern, sorgt das für entsprechende Geschwindigkeit, da nicht bei jeder Erstellung alle Bestandteile neu eingelesen und transformiert werden müssen.</p>

<p>Für die Zugriffe auf GET, POST, COOKIE und Header Informationen, gilt ähnlich zu Konfigurationen, dass die jeweiligen Funktionen mit und ohne Parameter aufgerufen werden können. Ist ein Parameter angegeben, wird der entsprechende Wert zurückgegeben. Fehlt der Parameter, werden alle Werte als Array geliefert.</p>

<h3>Response</h3>

<p>Am Ende einer jeden Anfrage steht dann schließlich noch die Herausforderung, eine Antwort an den Browser zu senden. Auch hier gilt es, immer wieder die gleichen Abläufe abzubilden:</p>

<ul>
<li>Setzen benötigter Header, wie z.B. Content-Type</li>
<li>Senden des Statuscodes und der Statusnachricht, z.B. 200 OK</li>
<li>Senden des eigentlichen Inhalts</li>
</ul>

<p>Zum Glück, gibt es auch dafür eine Abstraktionsklasse, die ihr per Dependency Injection anfordern könnt.</p>

<p>Im einfachsten Fall, sieht die Verwendung folgendermaßen aus:</p>

<pre><code class="php">$response = new Response();
$repsonse-&gt;send();
</code></pre>

<p>Ihr erzeugt eine Instanz der Klasse und ruft die Methode <code>send()</code> auf. Ohne weitere Einstellungen, wird in diesem Fall eine leere Antwort an den Browser geschickt. Der Statuscode ist dabei <code>200</code> und die Statusnachricht ist <code>OK</code>. Der Content-Type dieser Antwort wird auf <code>text/plain</code> gestellt und die Kodierung ist <code>utf-8</code>.</p>

<p>Alle diese Werte, könnt ihr bei Bedarf über entsprechende Methoden anpassen.</p>

<p>Für die Statuscodes gibt es ein vordefiniertes Enum. Wenn ihr den entsprechenden Code für den Repsonse setzt, wird automatisch auch gleich die Statusnachricht entsprechend dem <a href="https://tools.ietf.org/html/rfc7231#page-48">HTTP Standard</a> gesetzt.</p>

<pre><code class="php">$response-&gt;setCode(ResponseCodes::HTTP_CREATED); // 201 Created
</code></pre>

<p>Es gibt auch Hilfsmethoden, die das Setzen bestimmter Informationen für häufig benötigte Fälle vereinfachen. Ein Beispiel für eine solche Methode ist das Setzen von JSON Inhalt:</p>

<pre><code class="php">/**
 * @param array $data
 *
 * @return Response
 */
public function setJson(array $data): Response &#123;
    $this-&gt;headers&#91;'Content-Type'&#93; = 'application/json';
    $this-&gt;body = json_encode($data);

    return $this;
&#125;
</code></pre>

<p>Dieser Funktion könnt ihr ein Array mit Informationen übergeben, die automatisch konvertiert werden. Außerdem wird der Content-Type der Antwort gleich entsprechend gesetzt.</p>

<h3>Zusammenfassung</h3>

<p>Ihr habt in diesem Beitrag schon einiges erfahren. Viele, der hier beschriebenen Grundlagen, werdet ihr in den nächsten Beiträgen wiederfinden. Grundlagen sind zwar oft nicht so spannend, aber sie waren für mich während der Entwicklung auf jeden Fall sehr interessant und lehrreich. Was in der Verwendung oft einfach aussieht, muss doch gründlich vorbereitet werden. Es ist auch nicht ausgeschlossen, dass sich bestimmte Umsetzungen während der folgenden Entwicklungen noch verändern werden.</p>

<p>Jetzt will ich euch aber nicht länger aufhalten. Bis zum nächsten Mal.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[PHP-Framework: Eine Serie]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/php-framework-eine-serie" />
      <id>tag:https:2019:/next-direction.de/2.14</id>
      <published>2019-02-23T14:30:00Z</published>
      <updated>2019-03-01T18:52:46Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/framework.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/framework.jpeg" alt="Teaser - PHP-Framwork: Eine Serie"/><br>
        <p>Willkommen zurück! Ich habe einige Zeit nichts mehr von mir hören lassen. Das hat einen Grund. Ich habe mir die letzten Tage und Wochen den Kopf darüber zerbrochen, was ich hier als nächstes Schreiben soll. Obwohl meine bisherigen Beiträge vermuten lassen, dass ich eher auf Frontend spezialisiert bin, muss ich hier mal etwas klar stellen. Ich war die letzten Jahre eher in der Backend Entwicklung tätig und habe viel Spaß daran, mich stundenlang an bestimmten Algorithmen zu versuchen.</p>

<p>Die Liebe zur Frontend-Entwicklung ist erst über das letzte Jahr gewachsen, nachdem ich auf <a href="https://udemy.com">Udemy</a> diverse Kurse zu verschiedenen Themen durchgearbeitet habe. In der Serie, die ich mit diesem Beitrag einläuten will, geht es aber wieder Richtung Backend und sogar in sehr tiefe Tiefen.</p>

<p>Ich möchte mich nämlich eingehend mit den technischen Details hinter PHP-Frameworks beschäftigen und euch entsprechende Einblicke in die Entwicklung eines solchen Frameworks geben. Dazu werde ich das Projekt nach Abschluss auf GitHub zur Verfügung stellen.</p>

<h3>PHP - Meine Sprache für das Web</h3>

<p>Auch wenn in den letzten Jahren Sprachen wie Ruby, Python oder auch JavaScript (Node.js) in die Web-Entwicklung auf Serverebene Einzug gehalten haben, möchte ich doch eine Lanze für PHP brechen. Es ist die erste Sprache, die ich gelernt habe, damals noch in Version 4. Zu der damaligen Zeit waren Funktionalitäten, wie ich sie auch in dieser Serie vorstellen möchte, natürlich weit entfernt vom Machbaren. Techniken wie OOP, Namespaces oder Reflection waren höchstens in den Köpfen der Sprachentwickler oder in den Träumen mancher Entwickler vorhanden.</p>

<p>Spätestens mit PHP 7 sind nun alle Features vorhanden, die eine gute Sprache ausmachen. Auch an der Ausführgeschwindigkeit wurde extrem gearbeitet. Maßgeblich dafür war die Einführung eines strengeren Typsystems. Verbindet man das Ganze noch mit Caching über OPCache, dann merkt man nicht mehr viel davon, dass es sich bei PHP eigentlich um eine interpretierte und nicht kompilierte Sprache handelt. Es muss also bei jedem Aufruf einer Seite der komplette Quellcode interpretiert werden.</p>

<h3>Wieso ein eigenes Framework</h3>

<p>Nachdem ich über die letzten Jahre Projekte eher mit PhalconPHP umgesetzt habe, bin ich durch meine Kurse im letzten Jahr zu Symfony 4 gekommen. Was ich da gesehen habe, hat mich schon stark beeindruckt. Das Framework nimmt einem sehr viel - meist stupide und nervige - Arbeit ab und man kann sich auf die eigene Businesslogik fokusieren. Während ich mir Kurse darüber angeschaut und die Dokumentation durchgelesen habe, habe ich mich häufiger gefragt, wie die ganzen Sachen im Hintergrund funktionieren. Vor ein paar Wochen habe ich dann den Entschluss gefasst, zu Übungszwecken mein eigenes Framework zu schreiben.</p>

<p>Da ich bereits seit einiger Zeit daran arbeite, sind manche Bestandteile schon weiter fortgeschritten als andere und bestimmte Ideen schwirren mir bisher nur im Kopf herum. Da ich während meines Informatik Studiums oft mit Java entwickelt habe und mich die häufig nötige Konfiguration sehr gestört hat, habe ich mich dazu entschieden, wo nur möglich, auf Konfiguration zu verzichten. Ganz lässt es sich leider nicht vermeiden.</p>

<h3>Bestandteile</h3>

<p>Genug von den einführenden Worten. Kommen wir als nächstes zu den Komponenten des Frameworks und damit den Beiträgen dieser Serie, auf die ihr euch freuen könnt.</p>

<h5>Annotation Router</h5>

<p>Jedes Framework, egal ob Full-Stack oder nur Backend zur Entwicklung von Schnittstellen, benötigt Routing. Um das Ganze so einfach wie möglich zu machen, werden Routen direkt über PHP Annotationen in den Controller-Klassen definiert. Neben dem Router selbst gibt es dabei auch den Matcher, der die passende Route zu einer aufgerufenen URL sucht. Als letztes kommt dann der Dispatcher zum Einsatz, der die gewünschte Aktion ausführt und das Ergebnis zurückliefert.</p>

<p>Natürlich dürfen auch dynamische Routen nicht fehlen. Seit also gespannt!</p>

<h5>Dependency Injection</h5>

<p>Innerhalb der Controller-Klassen wird es DI geben. Dazu gebt ihr einfach über den Typ eines Parameters die benötigte Abhängigkeit an und sie wird euch geliefert. Wenn ihr also den Request benötigt, müsst ihr nicht lange suchen.</p>

<p>Für die Dependency Injection wird es auch einen Object Manager geben, der die Instanzierung übernimmt. Die aufgelösten Abhängigkeiten sind entweder als Singleton umgesetzt, oder werden bei jeder Verwendung neu erstellt. Das beste Beispiel für eine Singleton Instanz ist der Request. Dieser ändert sich während des kompletten Programmablaufs nicht und kann somit einmal erstellt und mehrere Male verwendet werden.</p>

<h5>Security Provider</h5>

<p>Routen müssen natürlich geschützt werden. Dazu wird es sog. Security Provider geben, die verschiedene Authentifizierungsmethoden unterstützen. Den Anfang wird ein JWT Provider machen, da ich das Framework überwiegend als Basis für eine API verwenden möchte, auf die per JavaScript Frontend zugegriffen werden kann.</p>

<p>Zur Absicherung einzelner Routen können dann bestimmte Rollen definiert werden, die Zugriff auf eine bestimmte Methode haben. Das Ganze kann auch auf Controller Ebene definiert werden, wodurch man sich viel Schreibarbeit sparen kann.</p>

<h5>Konsole</h5>

<p>Jedes vernünftige Framework braucht eine Komponente, um Konsolenkommandos entwickeln zu können. Mein Framework wird auch bereits fertige Kommandos beinhalten. Aber auch ihr erhaltet die Möglichkeit, für eure Fälle nötige Kommandos umzusetzen. Die Kernkomponente kümmert sich dabei um die lästige Arbeit, wie die Verarbeitung von Argumenten oder das Formatieren von Ausgaben.</p>

<h5>ODM für OrientDB</h5>

<p>Ich muss gestehen, bei dieser Komponente habe ich am längsten gebraucht, um mich für die Eigenentwicklung zu entscheiden. Das liegt vor allem daran, dass es in diesem Bereich mit Doctrine eine hervorragende Bibliothek gibt und ich Teile davon auch beruflich verwende. Ein Object-Document-Mapper sorgt dafür, dass eure Models in die Datenbank gespeichert werden.</p>

<p>Letzten Endes habe ich mich dann aber gefragt, wo wäre da denn der Spaß? Und auch wie schon für Routen, werde ich für die Definition innerhalb der Models auf Annotationen setzen. Ich gehe nach aktueller Planung davon aus, dass dieser Teil am aufwendigsten werden wird, weil er neben dem Mappen von Models auch teilweise die Abstraktion von Datenbankabfragen übernehmen wird.</p>

<p>Weil ich keine Lust darauf hatte den x-ten Wrapper für MySQL zu schreiben, habe ich mich direkt für OrientDB entschieden. Dabei handelt es sich um eine Graph Datenbank, die für mich Neuland darstellt aber hochinteressant klingt. Ich freue mich schon sehr darauf, hier in die Feinheiten einzutauchen und das Ganze etwas zu Abstrahieren.</p>

<h3>Fazit</h3>

<p>Die hier beschriebenen Komponenten sind fest eingeplant und zum Teil schon relativ weit fortgeschritten. Alles was sich während der Entwicklung ergibt, werde ich natürlich zu gegebener Zeit in den Beiträgen beschreiben.</p>

<p>Nun will ich euch aber nicht länger aufhalten! Ich hoffe ihr freut euch auch so sehr auf die kommenden Beiträge wie ich.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Full Stack JavaScript Framework]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/full-stack-javascript-framework" />
      <id>tag:https:2019:/next-direction.de/2.10</id>
      <published>2019-02-12T17:30:00Z</published>
      <updated>2019-08-31T06:50:35Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/stack.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/stack.jpeg" alt="Teaser - Full Stack JavaScript Framework"/><br>
        <p>Willkommen zurück! Heute möchte ich euch eines meiner Lieblingsprojekte der letzten Wochen und Monate vorstellen. Nachdem ich mich ausführlich mit Node.js, Nuxt.js und anderen Themen wie TypeScript beschäftigt habe, ist ein Stack entstanden, der euch schnell mit euren Projekten starten lässt. Neben den Grundbestandteilen wie Node.js und Fastify für die Serverseite und Nuxt.js für das Frontend, findet ihr unter anderem auch folgende Funktionalitäten:</p>

<ul>
<li>Axios für AJAX</li>
<li>Lodash inklusive Tree Shaking</li>
<li>Vuex mit Modulen und persistentem Status</li>
<li>LESS</li>
<li>Element UI als Frontend Framework inklusive Tree Shaking</li>
<li>i18n für Mehrsprachigkeit</li>
<li>FontAwesome 5 inklusive Tree Shaking</li>
<li>Moment Timezones</li>
<li>&#8230; und andere</li>
</ul>

<p>Ihr findet den Stack auf <a href="https://github.com/next-direction/nuxt-fastify">GitHub</a> und könnt ihn jederzeit für eigene Projekte nutzen. Das Ganze steht unter der MIT Lizenz. Nachdem ihr die Schritte aus der Readme Datei durchgeführt und damit den Server gestartet habt, werft als nächstes am besten einen Blick in die <code>Usage.md</code> Datei. Dort zeige ich ausführlich, wie die einzelnen Komponenten korrekt verwendet werden. Besonders bei den Komponenten mit Tree Shaking ist die richtige Verwendung wichtig.</p>

<h3>Tree Shaking kurz und knapp</h3>

<p>Falls ihr euch jetzt fragt, was dieses Tree Shaking ist, von dem ich jetzt schon einige Male gesprochen habe, dann lest die nächsten Zeilen. Bei jedem Node.js Projekt wird am Ende ein Build Prozess gestartet. Dieser fasst euren Code und alle eure Abhängigkeiten in einem Paket zusammen, um es auf einem Server veröffentlichen zu können. Dabei ist die Größe des fertigen Builds ein entscheidender Faktor für die Geschwindigkeit eurer Seite. Unter Tree Shaking versteht man die Reduzierung einer Abhängigkeit auf das Nötigste.</p>

<p>Nehmen wir als Beispiel FontAwesome 5. Es ist eine sehr umfangreiche Sammlung an verschiedensten Icons für eure Oberfläche. Durch die unzähligen Icons ist dieses Paket aber auch ziemlich groß. Wenn ihr in eurem Projekt von den tausenden von Icons aber nur zehn benötigt, wäre es kontraproduktiv, wenn trotzdem alle anderen Icons auch im finalen Produkt enthalten wären. Hier kommt Tree Shaking ins Spiel. Es wird automatisch von WebPack im Hintergrund erledigt, wenn ihr die einzelnen Icons wie in der <code>Usage.md</code> Datei beschrieben verwendet. Dabei werden nur die Icons in den Build einbezogen, die ihr eingebunden habt.</p>

<h3>Details einzelner Komponenten</h3>

<p>Obwohl in der <code>Usage.md</code> bereits einige hilfreiche Informationen zu den verwendeten Komponenten zu finden sind, will ich in den folgenden Abschnitten nochmal ausführlicher auf einzelne davon eingehen. Zum einen, um euch die Verwendung deutlicher zu machen, zum anderen aber auch, um euch Probleme zu erzählen, mit denen ich während der Zusammenstellung des Stacks zu kämpfen hatte.</p>

<h5>TypeScript</h5>

<p>Das Ziel bei der Zusammenstellung war es, alles über TypeScript zu entwickeln. Dazu zählten nicht nur eigene Dateien von Client und Server, sondern auch die Konfigurationen. TypeScript kommt in immer mehr Projekten zum Einsatz. Nachdem Angular mit Version 2 den Umstieg gewagt hat, wird Vue.js mit Version 3 auch komplett auf TypeScript setzen. Damit wird natürlich die Umsetzung eigener Projekte mit dieser Sprache auch vereinfacht, da man nicht mehr soviel Vorarbeit leisten muss, um das Framework an sich vorzubereiten. Wenn ich Projekte mit TypeScript umsetze, dann verwende ich immer Visual Studio Code. Die Unterstützung ist einfach sehr gut, weil Microsoft sowohl die Sprache als auch die Umgebung entwickelt und somit alles aus einer Hand kommt.</p>

<p>Die Entwicklung mit TypeScript bietet dabei unter anderem folgende Vorteile:</p>

<ul>
<li>Verbesserte Warnungen und Fehlermeldungen bereits in der Entwicklungsumgebung</li>
<li>Neueste Features noch bevor die Unterstützung durch Babel in JavaScript verfügbar ist</li>
<li>Vue.js Komponenten mit Klassen, Attributen und Methoden</li>
<li>Strenge Typisierung verringert Fehler zur Laufzeit</li>
</ul>

<p>Natürlich gibt es bei der Entwicklung auch einige Umstellungen und Sachen zu beachten, auf die ich während der Stackentwicklung gestoßen bin.</p>

<h6>Decorator</h6>

<p>Bei der Entwicklung von Vue Komponenten mittels JavaScript, sieht der Skript Teil üblicherweise in etwa so aus:</p>

<pre><code class="javascript">module.exports = &#123;
  data: function () &#123;
    return &#123;
      greeting: 'Hello'
    &#125;
  &#125;
&#125;
</code></pre>

<p>Im Beispiel seht ihr den anonymen Export eines JavaScript Objekts, der dann die nötigen Eigenschaften für eine Komponente beinhaltet. Wenn ihr die gleiche Komponente mittels TypeScript umsetzen möchtet, sieht das in etwa so aus:</p>

<pre><code class="javascript">@Component
class MyComp extends Vue &#123;
  greeting = 'Hello'
&#125;
</code></pre>

<p>Ich möchte die Aufmerksamkeit auf <code>@Component</code> richten. Dieses Sprachkonstrukt wird Decorator genannt. Es dekoriert also eine Klasse, ein Attribut oder eine Methode. Während der Kompilierung eurer Dateien, wird dann vom Compiler der nötige Code hinzugefügt, um es als normales JavaScript ausführen zu können. Denn der Browser versteht nun mal kein TypeScript.</p>

<p>Wenn ihr mit Nuxt.js und Vue.js Projekte in Verbindung mit TypeScript umsetzen möchtet, dann müsst ihr einige dieser Decorators kennen. Ich habe dazu die wichtigsten in der <code>Usage.md</code> meines Stacks verlinkt.</p>

<p>Abgesehen davon finde ich, die Komponente wirkt aufgeräumter als zuvor. Zudem können die Komponenten mit gewohnter objektorientierter Programmierung entwickelt werden.</p>

<h6>Zwei Modulsysteme</h6>

<p>Eine Problematik, die mich zu Beginn einige Zeit beschäftigt hat, waren die verschiedenen Modulsysteme von Node.js und Nuxt.js bzw. von Server und Client Seite.
Während Node.js für die Module auf <code>CommonJS</code> setzt, kommt im Frontend die EcmaScript 6 Version von Modulen zum Einsatz. Leider sind diese beiden Systeme nicht kompatibel und als ich mit der TypeScript Integration in den Stack begonnen habe, musste ich mich beim Kompilieren für eines der beiden entscheiden. Es hat sich aber schnell herausgestellt, dass das so nicht funktionieren kann.</p>

<p>Zum Glück bietet TypeScript die Möglichkeit, auch in Unterordnern eigene <code>tsconfig</code> Dateien anzulegen, die dann nach unten in der Ordnerstruktur vererbt werden. Letzten Endes gibt es also im Stack drei <code>tsconfig</code> Dateien. Eine Datei im <code>server</code> Ordner, die das <code>CommonJS</code> Modulsystem verwendet. Die zweite Datei im <code>client</code> Ordner, die das ES6 System verwendet. Die dritte und letzte Datei befindet sich im Hauptverzeichnis, da auch dort Konfigurationsdateien liegen, welche eine Übersetzung benötigen.</p>

<h5>Vuex Stores</h5>

<p>Was wäre ein größeres Vue.js Projekt ohne Vuex? Richtig, nicht viel, da es die Verwaltung des Anwendungszustands extrem vereinfacht.</p>

<p>Natürlich gibt es auch in Nuxt.js, welches in meinem Stack Verwendung findet, die Möglichkeit, Vuex Stores zu verwenden. Wie in der <a href="https://nuxtjs.org/guide/vuex-store">Nuxt Dokumentation</a> zu diesem Thema beschrieben, gibt es die Möglichkeit, den Status auf mehrere Module zu verteilen und somit die Zuständigkeiten besser zu trennen.</p>

<p>Im Folgenden möchte ich auf zwei Feinheiten eingehen, die in Verbindung mit Nuxt.js und mehreren Store Modulen wichtig sind.</p>

<h6>Persistenter Zustand</h6>

<p>Ich habe in meinen Stack ein Plugin integriert, das den Zustand einer Anwendung über das Neuladen der Seite hinaus erhält. Da es oft nicht sinnvoll ist, den kompletten Store zu erhalten, könnt ihr wichtige Werte wie die Benutzerauthentifizierung in der entsprechenden Konfigurationsdatei <code>plugins/vuex-persistent.ts</code> registrieren. Hier ein Beispiel, mit je einem Wert aus dem zentralen und einem modulspezifischen Store. Ihr könnt nämlich auch beide Konzepte mischen.</p>

<pre><code class="javascript">paths: &#91;
    'locale.current', // root state with embedded object value
    'list/count'      // module "list" state
&#93;
</code></pre>

<p>Am Beispiel vom zentralen Store, seht ihr auch, dass auch Unterwerte möglich sind.</p>

<h6>Die Funktion &#8220;nuxtServerInit&#8221;</h6>

<p>Wenn ihr euch bereits ein wenig mit Nuxt.js beschäftigt habt, dann kennt ihr die Funktion <code>nuxtServerInit</code> sicherlich. Diese Funktion wird von Nuxt beim serverseitigen Rendern eures Projekts aufgerufen und dient der Initialisierung eures Zustands. In Verbindung mit Store Modulen, habe ich eine Lösung erarbeitet, wie ihr auch innerhalb dieser Module eine solche Funktion implementieren könnt. Ich finde es einfach sauberer, wenn jedes Modul auch für die eigene Initialisierung zuständig ist. Deshalb will ich euch kurz zeigen, wie ihr das auch umsetzen könnt.</p>

<p>Der Startpunkt für die Initialisierung ist dabei die Funktion <code>nuxtServerInit</code> innerhalb des zentralen Stores, die von Nuxt.js automatisch bei jedem Laden der Seite aufgerufen wird. Der darin befindliche Code, könnte in etwa so aussehen:</p>

<pre><code class="javascript">// to simulate async action in the root store
const p = new Promise((resolve, reject) =&gt; &#123;
    resolve();
&#125;);

// use this for chaining init functions of store modules
return Promise.all(&#91;
    p,
    dispatch('list/nuxtServerInit', 20)
&#93;)
</code></pre>

<p>In diesem Zusammenhang ist es wichtig, zu wissen, dass wenn die Funktion ein <code>Promise</code> zurückgibt, Nuxt.js auf den Abschluss wartet, bevor die Seite erzeugt wird. In diesem Beispiel seht ihr gleich zwei Stellen, die ein <code>Promise</code> verwenden. Zum einen verwende ich ein eigens erstelltes <code>Promise</code>, um eine asynchrone Anfrage an einen Server zu simulieren, zum anderen verwende ich <code>Promise.all</code> um mehrere <code>Promises</code> zu einem neuen zusammenzufassen, das abgeschlossen ist, wenn alle anderen fertig sind. Häufig werden asynchrone Anfragen mit Axios durchgeführt, das in meinem Stack als Nuxt.js Modul registriert ist und das automatisch ein <code>Promise</code> zurückgibt.</p>

<p>Wie ihr in der vorletzten Zeile des obigen Beispiels seht, wird eine Aktion des Modul-Stores mit dem Namen <em>list</em> angestoßen. Dabei wird zu Demozwecken auch die Zahl 20 als Parameter übergeben. Die Aktion im zugehörigen Store, der dann unter <code>store/list.ts</code> definiert wäre, könnte zum Beispiel so aussehen:</p>

<pre><code class="javascript">export const actions = &#123;
    nuxtServerInit(context, test) &#123;
        return new Promise((resolve, reject) =&gt; &#123;
            context.commit('test', test);
            resolve();
        &#125;);
    &#125;
&#125;
</code></pre>

<p>Beachtet, der Name der Aktion ist frei wählbar. Um aber klar zustellen, wozu sie verwendet wird, macht es Sinn, den gleichen Namen wie im zentralen Store zu verwenden. Die Variable <code>test</code> beinhaltet dabei die Zahl 20, die übergeben wurde. Auch hier ist wichtig, die Funktion muss ein <code>Promise</code> zurückgeben, um in der Hauptfunktion zu wissen, wann eine asynchrone Aktion abgeschlossen ist.</p>

<h5>ElementUI</h5>

<p>Ich habe mich bei meinem Stack dazu entschieden, <a href="https://element.eleme.io/#/en-US">ElementUI</a> für das Frontend zu verwenden. Es bietet viele Standard HTML-Elemente als Vue.js Komponenten und damit erweiterten Möglichkeiten in Verbindung mit eigenen Komponenten an. Für genauere Definitionen der angebotenen Elemente verweise ich auf die <a href="https://element.eleme.io/#/en-US/component/installation">Dokumentation</a>, die wirklich sehr umfangreich ist.</p>

<p>Ich möchte an dieser Stelle aber kurz auf die richtige Verwendung innerhalb des Stacks eingehen, um Tree Shaking verwenden zu können.</p>

<p>Im Ordner <code>plugins</code> befindet sich eine Datei namens <code>element-ui.ts</code>. Dort müssen Komponenten registriert werden, die ihr in euren Projekten verwenden möchtet. Hier ein Beispiel:</p>

<pre><code class="javascript">import Vue from 'vue';
import &#123;
    Button, Select
&#125; from 'element-ui';

Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
</code></pre>

<p>Dieser Code registriert einen Button und eine Selectbox zur globalen Verwendung innerhalb eures Projekts. Im fertigen Paket sind dann auch nur diese beiden Bestandteile enthalten und somit wird die Größe erheblich reduziert.</p>

<p>Solltet ihr bestimmte Elemente nur in einer einzelnen Komponente benötigen, könnt ihr diese auch direkt dort importieren. Das ist speziell mit Blick auf Code Splitting eine Erwähnung wert, das von Nuxt.js automatisch vorgenommen wird.</p>

<h5>i18n und neue Sprachen</h5>

<p>Der aktuelle Stack beinhaltet in der minimalen Oberfläche bereits eine Selectbox ohne Styling, um zwischen den vorhandenen Sprachen zu wechseln. Dabei wird beim Laden der Seite immer nur die Sprache geladen, die auch ausgewählt ist. Wird die Sprache umgestellt, wird die entsprechende Datei nachgeladen. Die aktuelle Sprache wird auch per Cookie und im LocalStorage des Browsers vorgehalten, um ohne sichtbares Flackern die eingestellte Sprache zu laden.</p>

<p>Um eine neue Sprache hinzuzufügen, reicht es, die entsprechende JSON Datei unter <code>client/static/lang</code> anzulegen und in zwei Dateien zu registrieren. Die erste Datei ist <code>client/store/index.ts</code>:</p>

<pre><code class="javascript">export const state = () =&gt; (&#123;
    locale: &#123;
        all: &#123;
            de: "Deutsch",
            en: "English",
        &#125;,
        current: "",
        default: "de",
        // if you add new locales here, make sure you add it to nuxt.config.ts in wepack ContextReplacementPlugin config

    &#125;,
&#125;);
</code></pre>

<p>Dort wird die neue Sprache in das <code>locale</code> Objekt unter der Eigenschaft <code>all</code> eingefügt. An dieser Stelle kann auch die Standardsprache definiert werden, über die Eigenschaft <code>default</code>.</p>

<p>Die zweite Stelle befindet sich in der Registrierung von Nuxt.js Plugins, in der Datei <code>nuxt.config.ts</code>:</p>

<pre><code class="javascript">new webpack.ContextReplacementPlugin(/moment&#91;/\\&#93;locale$/, /en|de/)
</code></pre>

<p>Dort können neue Sprachen einfach über <code>|</code> gefolgt vom Kürzel eingefügt werden. Für die möglichen Sprachen, könnt ihr das <a href="https://github.com/moment/moment/tree/develop/locale">GitHub Repository</a> des Moment Projekts prüfen. Wichtig ist, die Kürzel an dieser und der oben genannten Stelle müssen übereinstimmen. Die Anpassung in dieser Datei ist wichtig, um auch von Moment.js nur die Sprachen zu laden, die benötigt werden. Leider ist es aktuell nicht möglich, die Sprache von Moment.js zu wechseln ohne die Seite neu zu laden.</p>

<h5>FontAwesome 5</h5>

<p>Über FontAwesome möchte ich nicht zu viele Worte verlieren. Im Grunde, handelt es sich um eine Bibliothek, die in der kostenlosen Variante über 1.000 Icons bietet. Um Tree Shaking nutzen zu können, ist eine spezielle Verwendung innerhalb der <code>*.vue</code> Dateien nötig.</p>

<p>Zuerst müsst ihr die gewünschten Icons einzeln importieren:</p>

<pre><code class="javascript">import &#123; faAddressBook &#125; from "@fortawesome/free-solid-svg-icons";

export default &#123;
    computed: &#123;
        fa() &#123;
            // here you have to return all imported icons
            return &#123;
                faAddressBook
            &#125;;
        &#125;
    &#125;
&#125;
</code></pre>

<p>Danach könnt ihr die Icons mit vollem Funktionsumfang wie folgt nutzen:</p>

<pre><code class="html">&lt;fa :icon="fa.faAddressBook" size="6x"&gt;&lt;/fa&gt;
</code></pre>

<p>Beachtet, dass der Name innerhalb der <code>computed</code> Funktion und der Tag Name nur zufällig beide <code>fa</code> heißen. Wenn ihr möchtet, könnt ihr den Tag Namen in der Datei <code>nuxt.config.ts</code> anpassen:</p>

<pre><code class="json">"nuxt-fontawesome": &#123;
    component: "fa",
    imports: &#91;&#93;,
&#125;
</code></pre>

<p>Hier seht ihr auch eine weitere wichtige Komponente für das Thema Tree Shaking. Die <code>imports</code> Eigenschaft ist standardmäßig leer. Das sorgt dafür, dass im Standard kein Icon geladen wird.</p>

<h5>Fastify und API Endpunkte</h5>

<p>Dieser Teil des Stacks kann optional dazu verwendet werden, eine API auf Basis von Fastify zu entwickeln. Manchmal ist es nicht nötig, da man nur das Frontend braucht und als Backend andere Dienste wie Firebase von Google oder ein Headless CMS wie Directus einsetzt. Aber auch in diesem Fall, kann es unter Umständen nötig sein, einen eigenen Server zu entwickeln. Immer dann, wenn ihr Passwörter bzw. API Zugriffsschlüssel verwalten müsst, sollten diese Daten nicht an die Clients geschickt werden. Stattdessen, legt ihr sie auf dem Server ab, macht einen Endpunkt in Fastify und schickt von dort dann die API Anfragen inkl. Passwort bzw. API Schlüssel.</p>

<p>Unter <code>server/api/routes.ts</code> seht ihr wie eine Route definiert ist. Wenn ihr eine umfangreiche API plant, dann solltet ihr die Routen in entsprechenden Unterdateien definieren. Diese müsst ihr in der Hauptdatei registrieren. Dazu habe ich ein auskommentiertes Beispiel eingefügt.</p>

<p>Hier ein relativ einfaches Beispiel, wie so eine zusätzliche Datei aussehen kann. Dabei handelt es sich um eine Login Methode, die ich für ein API Projekt benötigt habe:</p>

<pre><code class="javascript">const AuthController = require("../../controller/AuthController");

module.exports = async (fastify, opts) =&gt; &#123;
    fastify.post("/login", &#123;
        config: &#123;
            jwt: fastify.jwt,
        &#125;,
        schema: &#123;
            body: &#123;
                properties: &#123;
                    email: &#123; type: "string", format: "email" &#125;,
                    password: &#123; type: "string" &#125;,
                &#125;,
                required: &#91;"email", "password"&#93;,
                type: "object",
            &#125;,
            description: "Login a user",
            response: &#123;
                200: &#123;
                    description: "Successfull login",
                    properties: &#123;
                        token: &#123; type: "string" &#125;,
                    &#125;,
                    type: "object",
                &#125;,
                400: &#123;
                    description: "Invalid credentials",
                    type: "string",
                &#125;,
            &#125;,
            summary: "Login a user",
            tags: &#91;"Auth"&#93;,
        &#125;,
    &#125;, AuthController.login);
&#125;;
</code></pre>

<p>Der Vorteil an dieser sehr ausführlichen Routendefinition ist, dass ihr euch daraus unter anderem die entsprechende Dokumentation für die Endpunkte erstellen lassen könnt. Wer hier Interesse an weiteren Informationen hat, kann mich jederzeit per Kontaktformular, E-Mail oder über den neuen Slack Channel in der Fußzeile der Seite erreichen.</p>

<h5>Testing</h5>

<p>Das Testen eures Codes ist ein wichtiger Bestandteil eines jeden Projekts. In meinem Stack sind Testframeworks für Frontend und Backend integriert, inklusive Überprüfung der Testabdeckung. Für die Verwendung werft ihr am besten einen Blick in die <code>Usage.md</code>. Es gibt drei Kommandos, die für die Ausführung der Tests verwendet werden können:</p>

<pre><code class="bash"># run client tests
npm run test-client

# run server tests
npm run test-server

# run both test suites
npm run test
</code></pre>

<p>Durch die Möglichkeit, Client und Server Tests getrennt auszuführen, müsst ihr nicht immer auf die Ausführung aller Tests warten.</p>

<h5>Bonus: WebSockets und CI/CD</h5>

<p>Zum Abschluss der Vorstellung meines Stacks, möchte ich noch kurz zwei Bestandteile erwähnen, die je nach Projekt sehr interessant sein können.</p>

<h6>WebSockets</h6>

<p>Wenn ihr ein Projekt macht, das mit Echtzeitdaten arbeitet, werdet ihr um WebSockets nicht herum kommen. In der <code>Usage.md</code> Datei findet ihr ein Beispiel, wie ihr WebSockets mit meinem Stack verwenden könnt. Damit könnt ihr beispielsweise Chats oder Nachrichtencenter Funktionalität mit Echtzeit Updates realisieren.</p>

<h6>CI/CD</h6>

<p>Die Themen Continous Integration und Continues Delivery sind gerade sehr aktuell. Auch ihr werdet euch mit fortschreitender Entwicklung eurer Projekte damit befassen müssen. Als Startpunkt, habe ich in der <code>Usage.md</code> Datei ein Beispiel veröffentlicht, das bei mir bereits erfolgreich in einem Projekt eingesetzt wurde.</p>

<p>Ich habe zu diesem Zeitpunkt noch GitLab verwendet und auf einen <a href="https://github.com/dokku/dokku">Dokku</a> Server deployed. Bei jedem Git Push, werden dabei zuvor die Client- und Server-Tests ausgeführt und nach erfolgreichem Abschluss wird das Deployment gestartet. Ihr könnt das Ganze übersichtlich in der Oberfläche von GitLab verfolgen und es gibt auch Badges, die ihr in die Readme einbetten könnt, damit jeder, der euer Projekt anschaut, sofort sieht, was Sache ist.</p>

<h3>Fazit</h3>

<p>Ich hoffe, ich konnte euch einige Einblicke in meine Gedanken geben und euch dazu ermutigen mit dem Stack zu experimentieren. Auch wenn es manchmal ein paar Probleme gab, hat mir dieses Projekt sehr viel Spaß gemacht und immer wieder neue Herausforderungen beschert. Auch wenn ihr nicht den kompletten Stack verwenden wollt, gibt er euch doch viele Beispiele, wie bestimmte Pakete in das große Ganze eingefügt werden können. Ich werde versuchen die verwendeten Pakete in regelmäßigen Abständen zu aktualisieren.</p>

<p>Ihr könnt auch jederzeit selbst die Pakete überprüfen und aktualisieren lassen, falls ihr das in eurem Projekt wünscht. Führt dazu einfach folgendes Kommando aus:</p>

<pre><code class="bash">npm run upgrade
</code></pre>

<p>Damit sucht ihr nach Aktualisierungen und könnt sie bequem auswählen, nachdem ihr überprüft habt, ob es Änderungen gibt, die eure Code beeinflussen.</p>

<p>Solltet ihr Fragen oder Anregungen haben, könnt ihr gerne auch meinem neuen Slack Channel beitreten, den ich direkt nach dem Serverumzug von Next Direction eingerichtet habe. Ihr findet den Link dazu in der Fußzeile.</p>

<p>Nun will ich euch aber nicht länger aufhalten. Ich hoffe, wir sehen uns im nächsten Beitrag.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[In eigener Sache: Serverumzug]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/in-eigener-sache-serverumzug" />
      <id>tag:https:2019:/next-direction.de/2.13</id>
      <published>2019-01-27T09:30:00Z</published>
      <updated>2019-02-03T09:18:12Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/move.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/move.jpeg" alt="Teaser - Serverumzug"/><br>
        <p>Willkommen zurück! Heute will ich euch nur kurz darüber informieren, dass es die nächsten Tage bzw. Wochen ruhig auf Next Direction wird. Grund ist ein Serverumzug.</p>

<p>Als ich erfahren habe, dass der Umfang meines gebuchten Pakets zum gleichen Preis erhöht wurde, habe ich sofort <a href="https://contabo.de/">Contabo</a> kontaktiert und um eine Umstellung gebeten. Leider stellte sich heraus, dass ein automatischer Umzug bzw. eine Erhöhung der Ressourcen nicht so einfach ist. Ich habe mich daher dazu entschlossen, eine manuelle Migration durchzuführen.</p>

<p>Die gute Nachricht ist, Next Direction wird dadurch ein bisschen schneller. Wenn ihr diese Zeilen lest, ist die Seite auch bereits umgezogen und läuft auf dem neuen Server. In den nächsten Wochen werden dann andere Projekte folgen, die ich darauf laufen habe.</p>

<p>Ich versuche natürlich alles so schnell wie möglich über die Bühne zu bekommen, um euch schnellstmöglich mit neuen Inhalten versorgen zu können.</p>

<p>Jetzt will ich euch aber nicht länger aufhalten. Bis zum nächsten Mal.</p>

<p><strong>Update 03.02.2019</strong><br>
Der Umzug ist soweit abgeschlossen und verlief reibungslos.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Vue.js + Nuxt.js = Dreamteam]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/vuejs-nuxtjs-dreamteam" />
      <id>tag:https:2019:/next-direction.de/2.9</id>
      <published>2019-01-19T09:45:00Z</published>
      <updated>2019-02-09T08:21:05Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/dream.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/dream.jpeg" alt="Teaser - Vue.js + Nuxt.js = Dreamteam"/><br>
        <p>Willkommen zurück! Heute will ich euch zwei Frameworks vorstellen, die die Entwicklung von SEO-freundlichen Webanwendungen mit JavaScript extrem vereinfachen.</p>

<p><strong>Beim Ersten</strong> handelt es sich um <a href="https://vuejs.org/">Vue.js</a> (gesprochen wie das englische Wort <em>view</em>). Ich habe das erste Mal davon gehört, als die Version 2 frisch erschienen ist. Damals habe ich auch erste Versuche für eine eigene Anwendung unternommen, die jedoch aufgrund der noch ausbaufähigen Dokumentation gescheitert sind. Als ich im letzten Jahr dann auf <a href="https://www.udemy.com/">Udemy</a> aufmerksam geworden bin und dort viele Kurse über Vue.js angeboten werden, dachte ich mir, ich gebe dem Ganzen noch eine Chance. Und jetzt sitze ich hier und schreibe diesen Beitrag darüber. Ich habe mir insgesamt drei Kurse angeschaut, in jedem habe ich neue Sachen über das Framework gelernt. Ich habe mir auch Kurse zu <a href="https://angular.io/">Angular</a> und <a href="https://reactjs.org/">React</a> angeschaut und muss sagen, Vue.js gefällt mir eindeutig am besten. Es vereint aus technischer Sicht die Vorteile von beiden ohne die Nachteile zu übernehmen und der Start ist relativ einfach.</p>

<p>Vue.js als aufstrebenden Stern am Frameworkhimmel zu bezeichnen, wird dem Projekt mittlerweile gar nicht mehr gerecht. Auf GitHub hat es mittlerweile die meisten Sterne von den drei angesprochenen Frameworks. Ich habe auch eine sehr interessante Untersuchung zum Stand von JavaScript und den entsprechenden Frameworks gefunden. Das Ganze findet ihr <a href="https://2018.stateofjs.com/front-end-frameworks/overview/">hier</a>. Die zwei auffälligsten Punkte für mich waren dabei:</p>

<ul>
<li>Ungefähr ein Drittel der Entwickler, die schon mal Angular benutzt haben, würden es nicht mehr tun</li>
<li>Fast die Hälfte der befragten Entwickler, die schon mal von Vue gehört haben, möchten es lernen</li>
</ul>

<p><strong>Das zweite Framework</strong> setzt auf Vue.js auf und heißt <a href="https://nuxtjs.org/">Nuxt.js</a>. Dieses Framework wurde ursprünglich entwickelt, da JavaScript Frontends ein paar grundsätzliche Probleme haben. Es wird eine fast leere <code>index.html</code> Datei vom Webserver geladen und anschließend die komplette Oberfläche über JavaScript erzeugt. Zum einen muss man dem Besucher einer Seite in dieser Zeit einen Ladebildschirm anzeigen, zum anderen sieht eine Suchmaschine, die die Seite besucht, initial keinen Inhalt. Das heißt, die Seite kann nicht vernünftig indiziert werden.</p>

<p>Vue.js bietet zur Lösung beider Probleme das serverseitige Erzeugen der Seite an, im Englischen auch <em>server side rendering</em> (kurz SSR) genannt. Weil das Ganze in Vue aber sehr kompliziert einzurichten ist, wurde Nuxt.js ins Leben gerufen. Es bietet alle Vorteile von SSR ohne irgendwas konfigurieren zu müssen. Auch andere Konzepte wie z.B. das Routing werden vereinfacht, was dieses Framework zur idealen Ergänzung von Vue.js macht.</p>

<h3>Einführung in Vue.js</h3>

<p>Schauen wir uns zunächst Vue.js genauer an. Um den Beitrag nicht zu sehr ausarten zu lassen, werde ich einige grundlegende Konzepte vorstellen und euch jeweils auf die entsprechende Dokumentation verweisen, damit ihr euch eingehender damit beschäftigen könnt. Ich will auch gleich darauf hinweisen, dass größere Projekte zwingend einen Bundler wie <a href="https://webpack.js.org/">WebPack</a> benötigen, auf den ich hier jedoch nicht eingehen will. Dieser ermöglicht die Strukturierung des Quellcodes in mehrere Dateien, die für die produktive Installation dann zu einer Datei zusammengefasst werden. Außerdem ermöglicht er in Verbindung mit <a href="https://babeljs.io/">Babel</a> die Verwendung neuester JavaScript Funktionen, die dann je nach Zielbrowser entsprechend kompatibel gemacht werden.</p>

<h3>Unsere erste &#8220;App&#8221;</h3>

<p>Um mit der ersten Vue.js App beginnen zu können, legen wir zuerst einen Ordner an, ich nenne ihn <code>vue-basics</code>. Darin erstellen wir eine leere JavaScript Datei namens <code>app.js</code> und eine HTML Datei, die wir <code>index.html</code> nennen und die folgenden Inhalt aufweist:</p>

<pre><code class="html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="ie=edge"&gt;
    &lt;title&gt;Vue.js App&lt;/title&gt;
    &lt;script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div id="app"&gt;&lt;/div&gt;
    &lt;script src="app.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>

<p>Wie ihr seht, ist das überwiegend Standard Markup einer HTML5 konformen Webseite. Dazu kommen die beiden <code>script</code> Tags zum Einbinden von Vue.js und unserer eigenen JavaScript Datei, in der wir nun unsere erste Vue App erstellen werden. Um die App auf der Seite einhängen zu können, befindet sich im <code>body</code> noch ein <code>div</code> Element mit der ID <code>app</code>.</p>

<p>In die JavaScript Datei fügen wir nun folgenden Inhalt ein, der die minimale Logik für die Initialisierung unserer Vue.js App beinhaltet:</p>

<pre><code class="javascript">var app = new Vue(&#123;
    el: '#app'
&#125;)
</code></pre>

<p>Ihr könnt nun die <code>index.html</code> Datei über einen Doppelklick in eurem Standardbrowser öffnen. Für dieses einführende Beispiel brauchen wir nämlich keinen laufenden Webserver. Mit dem JavaScript Code, wird die App auf dem Element mit der ID <code>app</code> initialisiert.</p>

<h5>Vue Dev Tools</h5>

<p>Bevor wir nun mit der Programmlogik weiter machen, würde ich euch empfehlen, die <a href="https://github.com/vuejs/vue-devtools">Vue Dev Tools</a> für euren Browser zu installieren. Damit wird die Überwachung des Programmzustands und die Fehlersuche extrem erleichtert. Falls ihr Chrome verwendet, müsst ihr für die Erweiterung in den Einstellungen noch den Zugriff auf die Datei URLs erlauben. Das ist notwendig, da wir die Datei per Doppelklick und damit mit dem <code>file://</code> Protokoll geöffnet haben. Solltet ihr die <code>index.html</code> über einen Webserver öffnen, ist dieser Schritt nicht nötig.</p>

<p>Rechts oben im Browser sollte nun das Vue Logo erscheinen, das entsprechend gefärbt wird, wenn Vue.js auf einer Webseite erkannt wird. Wenn ihr nun die Entwicklerkonsole öffnet, findet ihr dort einen neuen Tab für die Dev Tools. Hier ein Bild, wie das Ganze im Chrome aussieht:</p>

<p><img src="https://next-direction.de/images/uploads/vue-console.jpg" alt="Entwicklerkonsole" /></p>

<p>In der oberen Leiste findet ihr auf der rechten Seite vier Icons zur Navigation. Damit könnt ihr zwischen den drei Bereichen <strong>Komponenten</strong>, <strong>Vuex</strong> und <strong>Events</strong> umschalten sowie die Anzeige aktualisieren. Wir werden uns in dieser Einführung überwiegend im ersten Bereichen <strong>Komponenten</strong> aufhalten.</p>

<p>Bei den Komponenten findet ihr nun unten auf der linken Seite den Strukturbaum eurer Komponenten, in unserem Fall bisher nur den Wurzelknoten <code>&lt;Root&gt;</code>. Auf der rechten Seite könnt ihr euch den Zustand und die Eigenschaften der einzelnen Komponenten anschauen, mehr dazu im nächsten Abschnitt.</p>

<h5>Reaktivität</h5>

<p>Unter Reaktivität versteht man das Verhalten, dass wenn sich der Zustand einer Komponente ändert, direkt auch die Anzeige in der Seite geändert wird. Von diesem Begriff leitet sich im Übrigen auch der Name des Frameworks <em>React</em> ab, das von Facebook entwickelt wird und von den Konzepten her sehr ähnlich zu Vue.js ist.</p>

<p>Um das Ganze in Aktion zu sehen, erweitern wir das <code>div</code> Element in unserem HTML Code um den Platzhalter <code>&#123;&#123; message &#125;&#125;</code>:</p>

<pre><code class="html">&lt;div id="app"&gt;
    &#123;&#123; message &#125;&#125;
&lt;/div&gt;
</code></pre>

<p>Um diesen Platzhalter mit der eigentlichen Nachricht zu füllen, erweitern wir den JavaScript Code wie folgt:</p>

<pre><code class="javascript">var app = new Vue(&#123;
    el: '#app',
    data: &#123;
        message: 'Hallo Welt mit Vue!'
    &#125;
&#125;)
</code></pre>

<p>Wenn ihr nun die Seite im Browser aktualisiert, werdet ihr mit der entsprechenden Meldung begrüßt. Das Ganze ist nun auch bereits reaktiv. Um das zu testen, öffnet ihr den Konsolen Tab der Entwicklertools eures Browsers und gebt Folgendes ein:</p>

<pre><code class="javascript">app.message = "Tschüss Welt mit Vue";
</code></pre>

<p>Nun sollte sich die Meldung auf der Webseite entsprechend geändert haben. Wie ihr seht, kann man über die Variable <code>app</code>, die bei der Erstellung der Vue App zugewiesen wird, auf den Status der App zugreifen und diesen von außen beeinflussen. Ihr könnt nun im Übrigen auch in den Vue Dev Tools euer <code>&lt;Root&gt;</code> Element anklicken und seht dann auf der rechten Seite den Zustand dieser Komponente.</p>

<p>An dieser Stelle will ich die allgemeine Einführung auch schon beenden. Solltet ihr euch weiter mit den grundlegenden Funktionen von Vue beschäftigen wollen, gibt es dazu sehr viele Abschnitte in der Dokumentation. Für den Einstieg empfehle ich euch das <a href="https://vuejs.org/v2/guide/">Get Started</a> Tutorial durchzuarbeiten. Dabei lernt ihr, wie Listen aus Arrays erstellt werden können, die bedingte Anzeige von Elementen funktioniert und die Eingaben von Benutzern in euren Komponenten verarbeitet werden können.</p>

<h5>Komponenten</h5>

<p><strong>Komponenten</strong> ist dabei das richtige Stichwort. Jede Vue.js App besteht aus unzähligen Komponenten. Jede Komponente sollte dabei so einfach wie möglich aufgebaut sein und für genau eine Aufgabe zuständig sein. Für diese Einführung will ich eine Komponente erstellen, die eine einfache ungeordnete Liste anzeigt.</p>

<p>Zum Anlegen dieser Komponente, fügt ihr folgenden Quellcode am Anfang der JavaScript Datei ein:</p>

<pre><code class="javascript">Vue.component('nd-list', &#123;
    data() &#123;
        return &#123;
            items: &#91;
                'Apfel',
                'Birne',
                'Orange'
            &#93;
        &#125;
    &#125;,
    template: `&lt;ul&gt;
                 &lt;li v-for="item in items"&gt;&#123;&#123; item &#125;&#125;&lt;/li&gt;
               &lt;/ul&gt;`
&#125;)
</code></pre>

<p>Damit wird eine Komponente namens <code>nd-list</code> global für die App registriert. Anders als bei der <code>Root</code> Komponente, wird <code>data</code> im Fall von allen weiteren Komponenten als Funktion umgesetzt. Das ist nötig, da es mehr als ein zugehöriges Element dieser Komponente geben kann. Würde <code>data</code> als Objekt umgesetzt sein, würden sich alle Elemente den gleichen Zustand teilen. Habt ihr also zwei Listen über diese Komponente erstellt und ihr ändert den <em>Apfel</em> in einer Liste zur <em>Zitrone</em>, würde auch die andere Liste geändert werden. Über die gezeigte <code>data()</code> Funktion, wird bei jeder Erstellung einer neuen Liste ein eindeutiges Objekt zurückgeliefert. Damit können die Zustände unabhängig voneinander verwaltet werden.</p>

<p>Da die registrierte Komponente initial an kein HTML Element gebunden ist, braucht sie selbst ein Template. In diesem Fall die gewünschte ungeordnete Liste. Darin wird eine sog. <a href="https://vuejs.org/v2/guide/syntax.html#Directives">Direktive</a> namens <code>v-for</code> verwendet, um aus den Elementen des Arrays eine entsprechende Liste zu erzeugen. Das Template einer Komponente muss übrigens immer ein eindeutiges Root Element haben, in unserem Fall <code>&lt;ul&gt;</code>. Es dürfen also z.B. keine zwei <code>div</code> Elemente auf oberster Ebene verwendet werden.</p>

<p>Im HTML Code kann die registrierte Komponente nun als benutzerdefiniertes HTML Tag wie folgt verwendet werden:</p>

<pre><code class="html">&lt;div id="app"&gt;
    &lt;nd-list&gt;&lt;/nd-list&gt;
&lt;/div&gt;
</code></pre>

<p>Wenn ihr die Seite aktualisiert, seht ihr die Obstliste, die ihr wiederum über die Vue Dev Tools in den Entwicklertools eures Browsers untersuchen könnt. Ihr könnt dort übrigens auch den Zustand (also die Liste des Obstes) verändern und sehen, wie sich das auf die Liste in der Anzeige auswirkt.</p>

<p>Auch zum Thema Komponenten könnte man stundenlang schreiben. Ich möchte hier aber wieder auf die sehr gute <a href="https://vuejs.org/v2/guide/components.html">Dokumentation</a> dazu auf der Homepage des Projekts verweisen.</p>

<h5>Routing</h5>

<p>Oft werden Anwendungen, die mit einem Framework wie Vue.js oder React umgesetzt werden, als sog. Single Page Application (kurz SPA) entwickelt. Das heißt, es wird vom Webserver immer die <code>index.html</code> Datei geliefert, den Rest erledigt das jeweilige Framework. Um nun trotz dieser einen Datei unterschiedliche Inhalte auf der Webseite anzuzeigen, wird Routing benötigt.</p>

<p>Für diesen Abschnitt reicht das Öffnen der <code>index.html</code> per Doppelklick nicht mehr aus. Wir brauchen dazu einen Webserver. Wer meinen Beitrag zu <a href="https://next-direction.de/posts/ausflug-in-die-welt-der-javascript-knoten">Node.js</a> gelesen hat, der weis, dass das mit Node.js kein Problem ist. Ich werde für die folgenden Ausführungen daher auch auf einen minimalen Server setzen, der für unsere Zwecke ausreicht. Ihr müsst dazu natürlich Node.js installiert haben. Folgt dazu am besten der Beschreibung in meinem anderen Beitrag.</p>

<p>Das verwendete Paket heißt <code>vue-http-server</code> und basiert auf dem sehr beliebten <code>http-server</code>. Es erweitert diesen um eine entscheidende Funktion. Sollte eine Route und damit eine Datei nicht gefunden werden, kann man eine Fallback Datei angeben, die stattdessen geliefert werden soll.</p>

<p>Um das nötige Paket global zu installieren, führt ihr auf der Konsole eures Betriebssystems folgenden Befehl aus:</p>

<pre><code class="bash">npm i -g vue-http-server
</code></pre>

<p>Nun wechselt ihr in dieser Konsole in den Ordner, in dem euer Projekt liegt und führt folgenden Aufruf aus, um den Server zu starten. Stellt zuvor sicher, dass Port 8080 auf eurem System nicht anderweitig belegt ist.</p>

<pre><code class="bash">http-server -f index.html
</code></pre>

<p>Wenn ihr nun im Browser <a href="http://localhost:8080">http://localhost:8080</a> aufruft, solltet ihr unser letztes Beispiel mit der Obstliste sehen.</p>

<p>Ich möchte an dieser Stelle nur sehr einfaches Routing zeigen, welches ihr auch in der Dokumentation von Vue findet. Es gibt ein sehr beliebtes Paket namens <a href="https://router.vuejs.org/guide/#html">Vue Router</a>, welches quasi der Standard für Routing mit Vue ist. Dieser bietet natürlich wesentlich mehr Umfang und Konfigurationsmöglichkeiten als dieses Beispiel. Für unseren Zweck reicht es aber aus.</p>

<p>Löscht zunächst in der HTML Datei den Inhalt aus dem <code>div</code> mit der ID <code>app</code>. Nun ersetzt ihr den Inhalt der JavaScript Datei mit folgendem Code, auf den ich gleich näher eingehen werde:</p>

<pre><code class="javascript">const NotFound = &#123; template: '&lt;p&gt;Seite nicht gefunden. &lt;a href="/"&gt;Zurück zur Startseite&lt;/a&gt;&lt;/p&gt;' &#125;
const Home = &#123; template: '&lt;p&gt;Startseite -&gt; &lt;a href="/about"&gt;Über uns&lt;/a&gt;&lt;/p&gt;' &#125;
const About = &#123; template: '&lt;p&gt;Über uns -&gt; &lt;a href="/"&gt;Startseite&lt;/a&gt;&lt;/p&gt;' &#125;

const routes = &#123;
  '/': Home,
  '/about': About
&#125;

new Vue(&#123;
  el: '#app',
  data: &#123;
    currentRoute: window.location.pathname
  &#125;,
  computed: &#123;
    ViewComponent () &#123;
      return routes&#91;this.currentRoute&#93; || NotFound
    &#125;
  &#125;,
  render (h) &#123; return h(this.ViewComponent) &#125;
&#125;)
</code></pre>

<p>Mit den ersten drei Zeilen, werden zunächst sehr einfache Templates erstellt. Es gibt also eine <em>Startseite</em>, eine <em>Über uns</em> Seite und eine Fehlerseite, falls die Route nicht gefunden wird. Bevor dann die eigentliche App initialisiert wird, werden noch die vorhandenen Routen definiert.</p>

<p>Im Vergleich zu unseren ersten Beispielen, gibt es zwei neue Eigenschaften, die eine App haben kann. Das erste sind sog. <a href="https://vuejs.org/v2/guide/computed.html#Computed-Properties">Computed Properties</a>. Sie sind sehr ähnlich zu den Eigenschaften in <code>data</code>. Der Unterschied besteht darin, dass Computed Properties von mehreren anderen Eigenschaften aus <code>data</code> abhängen können und die Anzeige immer aktualisiert wird, sobald sich eine der Abhängigkeiten verändert.</p>

<p>Die zweite Neuerung ist die <code>render</code> Funktion. Sie wird aufgerufen, wenn die App initialisiert wird. Ich möchte nicht zu sehr auf die Implementierungsdetails eingehen, aber im Wesentlichen gibt sie den initialen Inhalt für das <code>div</code> mit der ID <code>app</code> zurück.</p>

<p>Wer nun ein bisschen mit der Anwendung spielt und auch mal verschiedene Routen testet, die nicht existieren, wird schnell merken, dass jeder Aufruf ein neuer Request an unseren Server ist. Normalerweise will man das vermeiden und sollte das Ganze dazu mit der <a href="https://developer.mozilla.org/en-US/docs/Web/API/History">History API</a> des Browsers kombinieren.</p>

<p>Hier ein Ausschnitt, um unser bisheriges Beispiel darauf umzubauen:</p>

<pre><code class="javascript">const NotFound = &#123; 
    template: `&lt;p&gt;
                 Seite nicht gefunden. 
                 &lt;a href="javascript:;" onclick="app.goto('/', 'Startseite')"&gt;
                   Zurück zur Startseite
                 &lt;/a&gt;
               &lt;/p&gt;` 
&#125;
const Home = &#123; 
    template: `&lt;p&gt;
                 Startseite -&gt; 
                 &lt;a href="javascript:;" onclick="app.goto('/about', 'Über uns')"&gt;
                   Über uns
                 &lt;/a&gt;
               &lt;/p&gt;`
&#125;
const About = &#123; 
    template: `&lt;p&gt;
                 Über uns -&gt; 
                 &lt;a href="javascript:;" onclick="app.goto('/', 'Startseite')"&gt;
                   Startseite
                 &lt;/a&gt;
               &lt;/p&gt;`
&#125;

// ...

var app = new Vue(&#123;
  // ...
  methods: &#123;
    goto(path, title) &#123;
        this.currentRoute = path;
        history.pushState(&#123;&#125;, title, path);
    &#125;
  &#125;,
  // ...
&#125;)
</code></pre>

<p>Wenn ihr nun zwischen <em>Startseite</em> und <em>Über uns</em> wechselt, wird die URL getauscht, der Inhalt der Seite entsprechend geändert und all das, ohne einen Request abzuschicken. Dazu wird in der Methode <code>goto</code> sowohl die Eigenschaft unserer App für die Route als auch die URL Zeile des Browsers über die History API verändert.</p>

<h5>States</h5>

<p>Kommen wir zum letzten wichtigen Konzept, das man verstanden haben sollte, bevor man größere Projekte mit Vue.js umsetzt. Bei unseren bisherigen Beispielen war der Zustand (englisch <em>State</em>) einer Komponente direkt in dieser gespeichert. Der Zustand befindet sich dabei innerhalb des <code>data</code> Attributs.</p>

<p>Wenn ihr größere Projekte plant, wird es nicht ausbleiben, dass Komponenten ihren Status austauschen sollen. Dabei gibt es im Wesentlichen zwei Richtungen. Wenn übergeordnete Komponenten Teile ihres Zustands an untergeordnete weitergeben wollen, können sie dazu Attribute verwenden. Diese funktionieren wie ganz normale HTML Attribute und müssen von der untergeordneten Komponente in Empfang genommen werden. Gehen wir davon aus, wir wollen unserer Obstliste zusätzliche Informationen geben:</p>

<pre><code class="html">&lt;nd-list item-number="10"&gt;&lt;/nd-list&gt;
</code></pre>

<p>Standardmäßig wird das angegebene Attribut erstmal ignoriert, da unsere Komponente nicht weis, was sie damit anfangen soll. Werfen wir also einen Blick auf eine neue Eigenschaft, die unsere Komponente haben kann:</p>

<pre><code class="javascript">export default &#123;
    name: 'nd-list',
    props: &#91;'itemNumber'&#93;,
    data() &#123;
        return &#123;
            items: &#91;
                'Apfel',
                'Birne',
                'Orange'
            &#93;
        &#125;
    &#125;,
&#125;
</code></pre>

<p>Die neue Eigenschaft heißt <code>props</code>. Ihr müsst alle Attribute, die ihr innerhalb einer Komponente verwenden wollt, explizit in einer Liste erlauben. Beachtet auch, dass sich die Schreibweise in diesem Fall von der Definition im HTML Tag unterscheidet. Vue wandelt die Bindestriche dabei automatisch in Camel Case um, damit ihr einfacher innerhalb des JavaScript Codes damit arbeiten könnt.  Ihr könnt nun auf den Wert des Attributes zugreifen, als wäre es innerhalb der <code>data</code> Eigenschaft:</p>

<pre><code class="html">&lt;div&gt;
    Die Liste beinhaltet &#123;&#123; itemNumber &#125;&#125; Elemente.
    &lt;ul&gt;
        &lt;li v-for="item in items"&gt;&#123;&#123; item &#125;&#125;&lt;/li&gt;
    &lt;/ul&gt;
&lt;/div&gt;
</code></pre>

<p>Natürlich hätte dieses Beispiel auch einfacher umgesetzt werden können, da ihr jederzeit die Länge eines Arrays ermitteln könnt. Ich hoffe es verdeutlicht euch trotzdem, wie ihr Daten nach unten in eurem Komponentenbaum weitergeben könnt.</p>

<p>Was ist nun, wenn wir Daten nach oben weitergeben wollen? Dazu gibt es das Eventsystem. Eine untergeordnete Komponente kann ein Event auslösen, worauf eine übergeordnete reagieren kann. Bleiben wir wieder bei unserer Obstliste und lösen ein Event aus, wenn ein Benutzer auf ein Element der Liste klickt:</p>

<pre><code class="html">&lt;li v-for="item in items" @click="triggerEvent"&gt;&#123;&#123; item &#125;&#125;&lt;/li&gt;
</code></pre>

<pre><code class="javascript">export default &#123;
    name: 'nd-list',
    props: &#91;'itemNumber'&#93;,
    data() &#123;
        // ...
    &#125;,
    methods: &#123;
        triggerEvent: function(event) &#123;
            this.$emit('clickEvent');
        &#125;
    &#125;
&#125;
</code></pre>

<p>Dort wo die Liste verwendet wird, können wir das Event wie folgt verwenden:</p>

<pre><code class="html">&lt;nd-list @click-event="doSomething"&gt;&lt;/nd-list&gt;
</code></pre>

<p>Auch hier wandelt Vue die Schreibweise wieder automatisch zwischen Bindestrich und Camel Case um.</p>

<blockquote>
  <p>Super, ich weis jetzt, wie ich in beide Richtungen Daten austauschen kann. Bin ich damit bereit für größere Aufgaben?<br><br>
      &#45;&#45; ein vorschneller Entwickler</p>
</blockquote>

<p>Theoretisch ja, aber ich will vorher auf ein Problem hinweisen, das mit der gezeigten Vorgehensweise einhergeht. Stellt euch vor, ihr habt einen Komponentenbaum mit mehreren Komponenten, die verschiedene Verzweigungen nach unten haben.</p>

<p><img src="https://next-direction.de/images/uploads/componentTree.jpeg" alt="Entwicklerkonsole" /></p>

<p>Wollt ihr in diesem Beispiel zwischen den Komponenten 4 und 8 den Status austauschen, führt der Weg nur über 1. Dazu müsstet ihr eine Menge Events, Funktionen und Attribute definieren. Zum Glück gibt es andere Wege. Zwei möchte ich kurz nennen, um den Artikel nicht zu lang werden zu lassen aber nicht im Detail beschreiben.</p>

<ul>
<li><a href="https://medium.com/@andrejsabrickis/https-medium-com-andrejsabrickis-create-simple-eventbus-to-communicate-between-vue-js-components-cdc11cd59860">Event Bus</a>: Für mittelgroße Projekte hervorragend geeignet, um größere Distanzen zwischen Komponenten zu überbrücken. Dabei wird eine globale Komponente registriert, auf der ihr eure Events auslösen und in anderen Komponenten dann darauf reagieren könnt.</li>
<li><a href="https://vuex.vuejs.org/guide/">Vuex</a>: Zustand losgelöst von Komponenten. Definiert einen Zustand auf Anwendungsebene, auf den alle Komponenten zugreifen können. Benötigt einiges an Einarbeitung, aber ich verwende es persönlich für alle Projekte, da man nie weis, wie umfangreich eine Anwendung werden kann. Wenn man von Beginn an auf Vuex setzt, kann man die Architektur beliebig nach oben skalieren.</li>
</ul>

<h5>Single File Components</h5>

<p>Ihr könnt euch sicher vorstellen, dass bei einem größeren Projekt, die Umsetzung über die oben gezeigten Codeausschnitte schnell unübersichtlich werden kann. Vue bietet sog. Single File Components (kurz SFC) an. Dabei wird der komplette Quellcode (HTML, CSS und JavaScript) einer Komponente in einer Datei mit der Endung <strong>.vue</strong> zusammengefasst. Da das Ganze aber nur in Verbindung mit einem Bundler funktioniert, können wir das nicht direkt testen. Ich möchte euch ein Beispiel zeigen, das unsere Obstliste von oben als SFC umsetzt:</p>

<pre><code class="html">&lt;template&gt;
    &lt;ul&gt;
        &lt;li v-for="item in items"&gt;&#123;&#123; item &#125;&#125;&lt;/li&gt;
    &lt;/ul&gt;
&lt;/template&gt;
</code></pre>

<pre><code class="javascript">&lt;script&gt;
    export default &#123;
        name: "nd-list",
        data() &#123;
            return &#123;
                items: &#91;
                    'Apfel',
                    'Birne',
                    'Orange'
                &#93;
            &#125;
        &#125;,
    &#125;
&lt;/script&gt;
</code></pre>

<pre><code class="css">&lt;style scoped&gt;
    /** some component only css styling */
&lt;/style&gt;
</code></pre>

<p>Ihr müsst euch dabei alle drei Teile in einer Datei vorstellen. Leider konnte ich es wegen <a href="https://highlightjs.org/">Hightlight.js</a>, welches ich für Syntaxhighlighting verwende, nicht sinnvoll in einen Code Block zusammenfassen. Es gibt für mehrere Entwicklungsumgebungen aber Plugins, die mit dieser Syntax umgehen können und euch auch Highlighting und Auto-Vervollständigung anbieten.</p>

<p>Wie auch in der Einführung zu Komponenten gilt, innerhalb des <code>&lt;template&gt;</code> Tags dürft ihr nur ein Rootelement haben. Beachtet auch das Schlüsselwort <code>scoped</code> der Style Definition. Es sorgt dafür, dass alle Definitionen nur innerhalb dieser Komponente greifen. Im Hintergrund werden sie dazu einfach mit einem eindeutigen Prefix versehen. Ihr könnt euch das in den Entwicklerwerkzeugen eures Browsers genauer ansehen.</p>

<h3>Einführung in Nuxt.js</h3>

<p>Nun werfen wir einen kurzen Blick auf Nuxt.js. Wenn man ein neues Projekt damit erstellt, ist automatisch alles Wichtige konfiguriert. Nuxt gibt auch eine genau Ordnerstruktur vor, in die man seine Dateien einsortieren muss. Auch ein Bundler ist bereits integriert, der im Standard ohne Konfiguration auskommt. Ich möchte in den nächsten Zeilen aber weniger auf alle Feinheiten eingehen, sondern vielmehr die Konzepte, die wir bei Vue.js gesehen haben, auf Nuxt.js ummünzen.</p>

<p>Das Erste was es anzumerken gibt ist, dass Nuxt komplett auf SFC setzt. Alles was wir also in den folgenden Abschnitten machen, wird sich in <strong>.vue</strong> Dateien abspielen.</p>

<p>Damit ihr das Ganze nachverfolgen könnt, möchte ich euch kurz zeigen, wie ihr ein Projekt mit Nuxt.js erstellen könnt. Legt dazu einen Ordner eurer Wahl auf eurem Dateisystem an, ich nenne meinen <code>nuxt-basic</code>. Anschließend wechselt ihr auf der Kommandozeile in diesen Ordner und führt folgendes Kommando aus:</p>

<pre><code class="bash">npx nuxt-create-app .
</code></pre>

<p>Es installiert die nötigen Node.js Pakete und führt euch anschließend durch einen Assistenten, mit dem ihr eure Projekt anpassen könnt. Für unseren Fall reicht es, die Vorgaben zu akzeptieren. <code>npx</code> ist seit NPM 5.2 automatisch installiert.</p>

<p>Ihr könnt das Projekt über folgendes Kommando starten:</p>

<pre><code class="bash">npm run dev
</code></pre>

<p>Dadurch führt ihr einen Server im Entwicklungsmodus aus, der auf Änderungen an den Projektdateien reagiert und die Seite im Browser automatisch aktualisiert. Öffnet im Browser <a href="http://localhost:3000">http://localhost:3000</a>, um euer erstes Nuxt.js Projekt zu begutachten. Ihr erhaltet damit automatisch u.a. folgende Funktionen, ohne einen Finger zu krümmen:</p>

<ul>
<li>WebPack Bundler</li>
<li>Serverseitiges Erzeugen und damit SEO</li>
<li>Automatisches Routing über Ordnerstruktur</li>
</ul>

<p>Stürzt euch am besten in die <a href="https://nuxtjs.org/guide">Dokumentation</a> von Nuxt, um mehr über dieses geniale Framework zu erfahren. Es gibt auch einen sehr guten Kurs zu Nuxt von <a href="https://www.udemy.com/user/maximilian-schwarzmuller/">Max Schwarzmüller</a> auf Udemy. Im Folgenden möchte ich noch kurz auf ein paar Unterschiede in den Konzepten von Vue.js und Nuxt eingehen.</p>

<h5>Routing</h5>

<p>Während ihr bei Vue eure Routen über die Konfiguration festlegen müsst, erledigt Nuxt diese Aufgabe automatisch. Alle <strong>.vue</strong> Dateien, die ihr im Ordner <code>pages</code> anlegt, sind als Routen im Browser erreichbar. Auch Unterordner sind dabei möglich. Legt zum Test einfach eine Datei namens <code>about.vue</code> im genannten Ordner an, die folgenden Inhalt aufweist:</p>

<pre><code class="html">&lt;template&gt;
    &lt;div&gt;
        Meine erste eigene Seite mit Nuxt
    &lt;/div&gt;
&lt;/template&gt;
</code></pre>

<p>Wenn euer Entwicklungsserver noch läuft, dann solltet ihr in der Konsole sehen, dass das Projekt direkt neu erstellt wird und die neue Route damit bereits verfügbar ist. Ihr könnt das testen, indem ihr <a href="http://localhost:3000/about">http://localhost:3000/about</a> im Browser aufruft.</p>

<p>Einfacher geht es nicht, oder?</p>

<p>Die aufgerufene Seite sieht natürlich relativ leer aus. Oft benötigt man innerhalb eines Projekts ein Layout, das auf allen Seiten eines Projektes gleich ist. Zum Glück bietet Nuxt auch dafür eine Lösung, nämlich den <code>layouts</code> Ordner. Mehr dazu natürlich im <a href="https://nuxtjs.org/guide/views#layouts">entsprechenden Abschnitt</a> der Dokumentation, die übrigens selbst mit Nuxt.js erstellt ist. Klickt einfach ein bisschen darin rum und staunt über die Geschwindigkeit.</p>

<h5>States</h5>

<p>Es geht langsam dem Ende dieses Beitrags zu. Ich habe in den Ausführungen zu Vue.js kurz das Thema Vuex erwähnt. Es erlaubt den Status weg von den Komponenten und hin zur Anwendung zu verschieben. Damit lassen sich beliebig große Projekte sinnvoll verwalten. Nuxt bietet auch dafür einen Ordner namens <code>store</code>, indem ihr eure Vuex Stores definieren könnt. Ich möchte euch nochmal ermutigen, euch dieses Thema anzuschauen und für alle eure Projekte zu verwenden. Wenn man es einmal verstanden hat, ist es wirklich oft die Lösung von Problemen. Nuxt hat dazu einen eigenen Abschnitt in der <a href="https://nuxtjs.org/guide/vuex-store">Dokumentation</a>.</p>

<h3>Ausblick</h3>

<p>Ich freue mich bereits auf das aktuelle Jahr, beide Projekte haben umfassende Neuerungen angekündigt, was die Entwicklung noch mehr vereinfachen und beschleunigen wird. Der Plan für Vue.js sieht zumindest aktuell noch vor, dieses Jahr die Version 3 zu veröffentlichen. Es wird die erste Version, die komplett in TypeScript entwickelt ist. Außerdem wird es Verbesserungen der Ausführgeschwindigkeit und des reaktiven Verhaltens geben. Ich hoffe, das Team kann den strammen Zeitplan einhalten.</p>

<p>Für Nuxt.js ist auch Version 3 in Aussicht gestellt worden. Hier habe ich bisher jedoch keine genaueren Informationen gefunden, was geändert wird. Solltet ihr euch auf dem Laufenden halten wollen, empfehle ich euch beiden Projekten auf Twitter zu folgen, dort gibt es Neuigkeiten meist zuerst.</p>

<p>Ich hoffe, ich konnte euch die Entwicklung neuer Projekte mit diesen beiden Frameworks ein bisschen schmackhaft machen. Ich bin ein großer Fan geworden und die Ökosysteme beider Projekte sind stark am wachsen. Fast täglich lese ich von neuen Projekten, die damit umgesetzt werden.</p>

<p>Seid auf jeden Fall auch auf meinen nächsten Beitrag gespannt. Dort werde ich euch ein eigenes Projekt vorstellen, an dem ich einige Wochen gefeilt habe und das euch den Start in eure Projekte erleichtern wird. Mehr möchte ich aktuell noch nicht verraten.</p>

<p>Jetzt will ich euch aber nicht mehr länger aufhalten. Wir lesen uns im nächsten Beitrag!</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Ausflug in die Welt der JavaScript Knoten]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/ausflug-in-die-welt-der-javascript-knoten" />
      <id>tag:https:2019:/next-direction.de/2.8</id>
      <published>2019-01-04T08:40:00Z</published>
      <updated>2019-02-09T08:21:17Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/nodejs.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/nodejs.jpeg" alt="Teaser - Ausflug in die Welt der JavaScript Knoten"/><br>
        <p>Willkommen zurück! Heute geht es mal wieder etwas technischer zu. Über die Lernplattform Udemy, die ich in meinem Beitrag zur <a href="https://next-direction.de/posts/fortbildung-leicht-gemacht">Fortbildung</a> etwas näher beschreibe, bin ich zur Entwicklung mit Node.js gekommen. Was sich dabei zu Beginn als Segen herausstellte, wurde mit größer werdenden Projekten immer mehr zum Fluch. Es gibt viele Pakete auf denen man seinen eigenen Code aufbauen kann. Je mehr man davon verwendet, desto komplexer wird das Konstrukt und desto schwieriger wird es, diese Pakete immer auf dem Laufenden zu halten. Aber fangen wir vorne an.</p>

<h3>Was ist Node.js</h3>

<p>Node.js ermöglicht es mittels JavaScript, serverseitige Anwendungen zu entwickeln. Dazu wird die V8 Browserengine verwendet, die von Google ursprünglich für Chrome entwickelt wurde. Das besondere an Node.js ist zudem, dass alle blockierenden Aktionen, wie das Lesen und Schreiben von Festplatten oder auch Netzwerkzugriffe, in eigene Threads ausgelagert werden und somit zu nicht-blockierenden Aktionen werden. Das erlaubt trotz des Einzelprozesses in dem Node.js läuft, die Abarbeitung einiger tausend Abfragen in der Sekunde.</p>

<p>Node.js wurde 2009 in der ersten Version veröffentlicht und erfreut sich seitdem wachsender Beliebtheit. Während ich diesen Artikel schreibe (Anfang 2019) liegt die Version 11 vor und nicht immer war die Entwicklung von Harmonie geprägt. So wurde 2015 ein Fork namens io.js gegründet, da nicht alle Hauptentwickler mit der Richtung und der Geschwindigkeit des Projekts einverstanden waren. Diese Abspaltung war aber nur von kurzer Dauer und noch im selben Jahr wurde eine Stiftung gegründet unter deren Dach seitdem die Entwicklung abläuft.</p>

<h3><abbr title="Node Package Manager">NPM</abbr> - Das Herzstück</h3>

<p>Ich glaube die Plattform alleine wäre nicht da wo sie heute ist, wenn es nicht <abbr title="Node Package Manager">NPM</abbr> geben würde. Damit kann jeder Entwickler seine Pakete veröffentlichen und es anderen somit erlauben, diese in eigenen Angwendungen zu verwenden. Wer erfindet schon gerne das Rad jedes Mal neu?</p>

<p>Unter <a href="https://www.npmjs.com/">npmjs.com</a> kann man die Pakete durchsuchen, sich Statistiken zur Verwendung und der Pflege sowie die Versionen eines Pakets anschauen. In den Anfängen musste man sowohl Node.js als auch <abbr title="Node Package Manager">NPM</abbr> separat installieren. Seit einigen Versionen ist aber <abbr title="Node Package Manager">NPM</abbr> automatisch auf dem System installiert, sobald man Node.js eingerichtet hat.</p>

<h3>Grundlagen</h3>

<p>Schauen wir uns nun also an, wie das Ganze funktioniert. Dazu müsst ihr zuerst <a href="https://nodejs.org/en/">Node.js</a> installieren. Über den Link gelangt ihr zur Startseite des Projekts, wo es immer zwei Versionen zur Auswahl gibt. Auf der linken Seite findet ihr die Version, die längeren Support bietet, während auf der rechten Seite die Version zu finden ist, die die neuesten Funktionen beinhaltet. Für die folgenden Ausführungen ist es egal, welche Version ihr installiert. Sobald ihr die Installation abgeschlossen habt, öffnet ihr am besten die Konsole eures Betriebssystems und führt diese Anweisungen aus:</p>

<pre><code class="bash">node -v # v11.3.0
npm -v  # 6.4.1
</code></pre>

<p>Als Kommentar hab ich meine aktuellen Versionen angegeben, damit ihr seht, wie die Ausgabe aussehen sollte. Wenn ihr eine Fehlermeldung bekommt, dass eines der Kommandos nicht verfügbar ist, müsst ihr nochmals eure Installation prüfen. Gegebenenfalls findet ihr auch Hilfe im Internet.</p>

<p>Als nächstes müsst ihr einen neuen Ordner anlegen, indem wir ein kleines Projekt mit dem <a href="https://www.fastify.io/">Fastify</a> Web-Framework erstellen werden. Fastify ist ein junges aufstrebendes Projekt, das gegenüber bekannteren Vertretern wie Express ein paar Vorteile bietet:</p>

<ul>
<li><strong>Hochperformant:</strong> Das Projekt wirbt abhängig von der eigenen Codekomplexität von bis zu 30.000 Anfragen in der Sekunde</li>
<li><strong>Erweiterbar:</strong> Fastify bietet Hooks, Plugins und Decorator an, um den Funktionsumfang zu erweitern</li>
<li><strong>Schema-basiert:</strong> Auch wenn es keine Pflicht ist, kann man den <a href="http://json-schema.org/">JSON-Schema</a> Standard verwenden, um Routen zu validieren</li>
<li><strong>Logging:</strong> Normalerweise eine zeitintensive Aufgabe, nicht so bei Fastify, dort kommt ein Paket zum Einsatz, das die benötigte Zeit extrem reduziert</li>
</ul>

<p>Achtet bei der Erstellung des Ordners darauf, dass der Name sich nicht mit einem Paket überschneidet, welches ihr verwenden wollt z.B. fastify in unserem Fall. Nachdem ihr in der Konsole in diesen Ordner gewechselt habt, gebt ihr folgenden Befehl ein:</p>

<pre><code class="bash">npm init
</code></pre>

<p>Es werden euch als nächstes einige Fragen zum Projekt gestellt, die ihr für dieses erste Beispiel mit den Standardwerten - welche in Klammern stehen - bestätigen könnt. Zum Abschluss wird euch dann der Code der <code>package.json</code> Datei angezeigt, die erstellt wird sobald ihr mit [Enter] bestätigt. Diese Datei ist der Hauptbestandteil jedes Node.js Projekts. Darin stehen nämlich nicht nur die Informationen zu eurem Projekt, sondern mit zunehmender Entwicklungsdauer auch alle Abhängigkeiten, die euer Projekt hat. Mit dieser Datei ist es jedem anderen Entwickler möglich, die nötigen Pakete zu installieren.</p>

<h5>Pakete installieren</h5>

<p>Die Basis ist also geschaffen, nun müssen wir natürlich noch Pakete installieren, denn sonst ist unser Projekt relativ sinnfrei. Es gibt zwei Möglichkeiten ein Paket zu installieren. Entweder als Produktivabhängigkeit</p>

<pre><code class="bash">npm i -S fastify
</code></pre>

<p>oder als Entwicklungsabhängigkeit</p>

<pre><code class="bash">npm i -D mocha
</code></pre>

<p>Für unser Projekt braucht ihr jetzt nur den ersten Befehl ausführen und damit fastify installieren. Ich habe zu Demozwecken beide Befehle bei mir ausgeführt, was zu folgenden Abschnitten in der <code>package.json</code> Datei führt:</p>

<pre><code class="json">"dependencies": &#123;
  "fastify": "^1.13.3"
&#125;,
"devDependencies": &#123;
  "mocha": "^5.2.0"
&#125;
</code></pre>

<p>Unter <code>dependencies</code> befinden sich alle Pakete, die unser Projekt für den produktiven Einsatz benötigt, während unter <code>devDependencies</code> die Pakete für die Entwicklungsphase aufgeführt sind. Das angegebene <code>mocha</code> Paket ist übrigens für Tests zuständig.</p>

<h5>Paketversionen</h5>

<p>Es gibt ein paar Punkte, die man beachten sollte, wenn es um die Versionen von Paketen geht. Wenn ihr ein Paket ohne Angabe einer Version installiert, wird immer die aktuellste vom Entwickler als stabil gekennzeichnete Version installiert. Der Eintrag in die <code>package.json</code> Datei wird automatisch von <abbr title="Node Package Manager">NPM</abbr> vorgenommen und es wird ein <code>^</code> vorangestellt. Hier der Befehl, um zum Beispiel immer die aktuellste Version von fastify Version 1 zu installieren:</p>

<pre><code class="bash">npm i -S fastify@1
</code></pre>

<p>Der Eintrag in der <code>package.json</code> verändert sich in diesem Fall nicht. Ihr könntet auch direkt <code>@1.13.3</code> angeben und es würde dann mit jedem Aufruf von <code>npm install</code> (was immer alle Abhängigkeiten installiert), die aktuellste fastify Version installiert. In der Regel ist das kein Problem, wenn sich der Entwickler eines Pakets an die Konventionen hält und nur mit Hauptversionen Änderungen vornimmt, die nicht abwärtskompatibel sind. Ihr könnt die Versionen aber auch genauer festlegen. Dazu gibt es folgende Möglichkeiten:</p>

<ul>
<li><strong>1.13.3</strong> - Angabe ohne Sonderzeichen: Es wird immer genau diese Version installiert</li>
<li><strong>1.13</strong> oder <strong>1.13.x</strong> oder <strong>~1.13.3</strong>: Diese Angaben führen dazu, dass immer die aktuellste Patch Version installiert wird. Das ist der letzte Teil der Nummer und beinhaltet in der Regel wichtige Fehlerbehebungen, die ihr natürlich haben wollt</li>
<li><strong>1</strong> oder <strong>1.x</strong> oder <strong>^1.0.4</strong>: Standardfall: Damit werden automatisch alle Funktionsreleases (über die mittlere Nummer abgebildet) installiert. Mit solchen Releases werden kleinere Funktionen hinzugefügt, bestehende aber nicht verändert.</li>
<li><strong>&#42;</strong> oder <strong>x</strong>: Neueste Versionen: Mit Vorsicht zu genießen, da ihr damit keinen Einfluss auf die Version habt und unter Umständen euer Projekt nicht mehr läuft, wenn ihr die Pakete aktualisiert.</li>
</ul>

<p>Ihr müsst natürlich von Fall zu Fall entscheiden, welche Version eines Pakets sinnvoll ist. Am besten schaut ihr euch auch immer die Releasenotes des jeweiligen Pakets an, um besser beurteilen zu können, welches Schema für dieses Paket am besten geeignet ist.</p>

<h3>Startpunkt</h3>

<p>Für diese Einführung brauchen wir erstmal keine weiteren Pakete. Der nächste Schritt besteht darin, den Einstiegspunkt von Fastify zu definieren. Dazu legt ihr im Basisverzeichnis eures Projekts eine Datei an, die ihr frei benennen könnt. Ich orientiere mich hier an der Dokumentation von Fastify und nenne sie <code>server.js</code>. In diese Datei fügt ihr folgenden Inhalt ein:</p>

<pre><code class="javascript">// Import des benötigten Pakets
const fastify = require('fastify')()

// Definition der ersten Route
fastify.get('/', (request, reply) =&gt; &#123;
  reply.send(&#123; hello: 'world' &#125;)
&#125;)

// Starten des Servers
fastify.listen(3000, (err) =&gt; &#123;
  if (err) &#123;
    fastify.log.error(err)
    process.exit(1)
  &#125;
  fastify.log.info(`server listening on $&#123;fastify.server.address().port&#125;`)
&#125;)
</code></pre>

<p>Ihr startet den Server mit folgendem Kommando in der Konsole:</p>

<pre><code class="bash">node server
</code></pre>

<p>Und das war es auch schon. Ihr könnt im Browser eurer Wahl zur Adresse <a href="http://localhost:3000">http://localhost:3000</a> navigieren und solltet die JSON-kodierte Version von <em>Hallo Welt</em> sehen.</p>

<p>Natürlich ist das nur ein sehr einfaches Beispiel, aber es zeigt, wie schnell man mit Node.js eigene Projekte beginnen kann. Dieses Beispiel kann man jetzt beliebig erweitern. Hier eine Liste möglicher Zusätze:</p>

<ul>
<li><strong>Neue Routen:</strong> Ihr könnt weitere GET-/POST-Routen hinzufügen und ausprobieren</li>
<li><strong>Statische Dateien:</strong> Ressourcen wie Bilder, CSS-Dateien oder JavaScript-Dateien werden statisch ausgeliefert. Ihr könnt versuchen, diese Funktion über die Dokumentation von Fastify zu finden und umzusetzen</li>
<li><strong>Verwenden von Plugins:</strong> Fastify bietet eine lange Liste an Plugins, um bestimmte Funktionen nachzurüsten. Seht euch die Liste über das Ökosystem von Fastify an und probiert für euch interessante Funktionen umzusetzen</li>
</ul>

<h3>Nächste Schritte</h3>

<p>Ich wollte euch in diesem Beitrag lediglich einen kurzen Einstieg in die wunderbare Welt der JavaScript Knoten geben. Dieses Thema ist jedoch so umfassend, dass darüber ganze Bücher geschrieben werden können. Wenn ihr euch weiter mit dem Thema beschäftigen wollt, gibt es natürlich einige Möglichkeiten. Ihr könnt euch in die Dokumentationen von Node.js, <abbr title="Node Package Manager">NPM</abbr> und auch Projekten wie Fastify stürzen. Oder ihr macht es wie ich und sucht euch Videokurse zu den Themen die euch interessieren. Wenn ihr interessiert seid, könnt ihr mich jederzeit über das Kontaktformular im Fußbereich kontaktieren und ich kann euch gute Kurse auf Udemy empfehlen.</p>

<p>Ich persönlich finde Node.js eine extrem spannende Geschichte. Leider musste ich aber auch schon einige Rückschläge einstecken, die meinen Enthusiasmus etwas gedämpft haben. Je tiefer man in die Materie einsteigt und je ausgiebiger man bestimmte Pakete in seinen eigenen Projekten verwendet, desto schneller stößt man an Grenzen. Ich habe deshalb schon einige Pakete über GitHub <a href="https://de.wikipedia.org/wiki/Abspaltung_(Softwareentwicklung)">forken</a> müssen, um Anpassungen vornehmen zu können, die ich zwingend gebraucht habe. Oft sind es Fehler in den entsprechenden Paketen gewesen, aber auch fehlende Funktionen habe ich bereits eigenhändig nachpflegen müssen.</p>

<p>Ich werde euch in einem der nächsten Beiträge eines dieser Projekte vorstellen. Dazwischen werde ich noch den ein oder anderen Beitrag als Vorbereitung einschieben. Bleibt also gespannt!</p>

<p>Nun will ich euch aber nicht mehr länger aufhalten. Ich hoffe, ich konnte euch ein wenig für Node.js begeistern. Letztendlich müsst ihr nun für euch entscheiden, ob ihr dem Ganzen eine Chance gebt.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Edge trifft Chromium: Gedankenspiele]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/edge-trifft-chromium-gedankenspiele" />
      <id>tag:https:2018:/next-direction.de/2.7</id>
      <published>2018-12-29T08:30:00Z</published>
      <updated>2019-01-19T19:27:49Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/chrome.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/chrome.jpeg" alt="Teaser: Edge trifft Chromium: Gedankenspiele"/><br>
        <p>Willkommen zurück! Heute geht es zum letzten Mal theoretisch zu, versprochen. Microsoft hat vor einigen Wochen eine <em>Bombe</em> platzen lassen und verkündet, den Edge Browser in Zukunft auf <a href="https://www.chromium.org/Home">Chromium</a> basieren zu lassen.  Wenn ich zurückdenke, waren meine ersten Gedanken dazu sehr positiv. Ich bin ja nun schon einige Jahre als Web-Entwickler unterwegs und jeder der schon einmal eigene Seiten für z.B. Internet Explorer 7 optimieren musste, kann das sicher nachvollziehen. Der Konzern aus Redmond hat gerade beim Thema Browser nicht immer ein glückliches Händchen bewiesen.</p>

<p>Als während der Insider Phase von Windows 10 klar wurde, dass es auch einen neuen Browser namens <strong>Edge</strong> geben wird, war ich zugegebenermaßen erstmal erleichtert. Meine ersten privaten Entwicklungen musste ich damals noch für Internet Explorer 6 optimieren. Wer kennt sie nicht die ganzen Bedingungen im HTML-Markup, um nur spezielle Versionen des Internet Explorers anzusprechen. Die letzten Jahre wurde es dann um einiges besser, als der Internet Explorer 11 zum einzig unterstützen Browser von Microsoft wurde. Dieser beherrscht viele der moderneren CSS Eigenschaften und auch JavaScript Funktionen.</p>

<p>Edge hob das Ganze auf ein ganz neues Level. Bei vielen Techniken ist er auf Augenhöhe mit Vertretern der Open Source Szene, so machte auch Web-Entwicklung wieder Spaß. Auch die Firma in der ich beschäftigt bin, hat sich dazu entschlossen, die Unterstützung des Internet Explorers Anfang des kommenden Jahres einzustellen.</p>

<h3>Ade Internet Explorer</h3>

<p>Das bringt mich zum ersten Punkt den ich sehr begrüße an der Entscheidung. Mit der Umstellung auf Chromium als <a href="https://de.wikipedia.org/wiki/HTML-Renderer">Rendering Engine</a>, wird nicht nur die Browserlandschaft einheitlicher - mehr dazu auch später, sondern es werden auch ältere Windows Versionen wie 7 und 8 unterstützt. Damit gibt es (fast) keinen Grund mehr, den Internet Explorer noch freiwillig einzusetzen. Fast, weil es in vielen größeren Firmen üblich ist, die Computersysteme zentral zu verwalten und vorzuschreiben welche Software installiert wird. Oft verzichten diese Firmen auf die Installation fremder Browser und schreiben den Internet Explorer vor. Auch viele Anwendungen auf Basis von C# verwenden die Engine des Internet Explorers, um HTML Inhalte darzustellen. Oft ist das auch ein Grund für Firmen, ewig und drei Tage an diesen alten Versionen festzuhalten.</p>

<p>Wenn man sich die Browserstatistiken der letzten Jahre ansieht, dann ist die Quote des Internet Explorers stark rückläufig und wird in naher Zukunft unter die 10% Marke fallen. Ich hoffe, dass der nun angekündigte Schritt diese Entwicklung noch stärker beschleunigen wird, damit wir diese ganzen Altlasten hinter uns lassen können.</p>

<h3>Willkommen Standards</h3>

<p>Normalerweise heißt es ja, Konkurrenz belebt das Geschäft. Diese These unterstütze ich natürlich auch, aber nicht in Bereichen, in denen es um die Umsetzung von Standards geht, sondern eher wenn es um den Preiskampf von Produkten geht. Bei der Umsetzung von Standards gilt für mich eher, viele Köche verderben den Brei. Die Vergangenheit hat gezeigt, dass viele Browserhersteller ihre ganz eigene Interpretation des Standards haben. Google war hier mit dem Chromium Projekt noch am ehesten am Standard dran. Leider haben sogar Firefox und Safari manchmal ein Eigenleben, wenn es um speziellere Funktionen geht.</p>

<p>Mit dem Schritt Microsofts, auf Chromium zu setzen, wird zwar eine weitere Engine - übrigens EdgeHTML genannt - verschwinden, der Standard damit aber wohl weiter vorangetrieben. Der Konzern aus Redmond hat nämlich angekündigt, sich an der Entwicklung von Chromium aktiv zu beteiligen.</p>

<p>Wenn ich nun einen Nachfolger küren müsste, wenn es um die suboptimale Umsetzung des Standards geht, wäre das wohl Safari. Gerade in den letzten zwei bis drei Jahren hat sich gezeigt, dass die Entwicklung hier dem Standard oft weit hinterherhinkt. Lasst uns also hoffen, dass wir hier nicht den nächsten Internet Explorer an der Backe haben.</p>

<h3>Schwieriger Stand</h3>

<p>Wenn man sich die Geschichte von Edge anschaut, war diese auch alles andere als glücklich. Leider wurde der Browser in meinen Augen unvollständig veröffentlicht. Niemand wollte einen Browser verwenden, bei dem Basisfunktionen fehlten, erst recht nicht, wenn man sich über die Jahre an den Luxus von abertausenden Erweiterungen bei anderen Browsern gewöhnt hat. So kommt es nicht überraschend, dass auch heute noch die Verbreitung bei erst ca. 5% liegt und das, obwohl es sich um den Standardbrowser von Windows 10 handelt, einem System, das sich langsam aber sicher einer Milliarde installierter Systeme nähert.</p>

<h3>Schattenseiten</h3>

<p>Ich will natürlich nicht verschweigen, dass nicht alles an Edge schlecht war. Ich verwende Edge zum Beispiel sehr gerne auf meinen Tablets, da er eine wesentlich bessere Leistung bietet als der Rest. Seit Erweiterungen über den Store unterstützt werden, fehlt mir zumindest für die private Anwendung auch keine Funktion mehr. Beruflich konnte ich mich leider nie mit der Entwicklerkonsole anfreunden und glaubt mir, ich habe es immer wieder versucht.<br>
In Sachen Akkulauftzeit ist er laut Aussagen von Microsoft unangefochten an der Spitze. Ich selbst habe diesen Sachverhalt bisher aber nicht überprüft.</p>

<p>Leider fallen mir gerade keine weiteren positiven Punkte mehr ein, solltet ihr noch andere Gedanken zur Thematik haben, könnt ihr mich gerne jederzeit über das Kontaktformular erreichen.</p>

<p>Jetzt will ich euch aber nicht länger aufhalten. Es hielt sich ja zum Glück in Grenzen heute.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Fortbildung leicht gemacht!]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/fortbildung-leicht-gemacht" />
      <id>tag:https:2018:/next-direction.de/2.6</id>
      <published>2018-12-27T09:00:00Z</published>
      <updated>2019-01-19T19:27:58Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/learning.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/learning.jpeg" alt="Teaser - Fortbildung leicht gemacht!"/><br>
        <p>Willkommen zurück! Heute möchte ich auf etwas eingehen, das mich das komplette Jahr beschäftigt hat und mich auch in Zukunft weiter beschäftigen wird. Außerdem handelt es sich um ein Thema, das ausschlaggebend dafür war, dass ich diesen Blog gestartet habe. Lange Rede kurzer Sinn, es geht um Fortbildung.</p>

<h3>Wieso, weshalb, warum</h3>

<p>Falls ihr euch jetzt fragt, warum ich mich damit auseinandergesetzt habe und wieso ihr das auch machen solltet, dann lest schnell weiter. Ich habe nämlich tatsächlich mal von einem Bewerber gehört, der auf die Thematik der Fortbildungen angesprochen wurde, dass man das doch nicht müsse. Und ich muss zugeben, dass ich auch über die letzten Jahre etwas bequem geworden bin. Wenn ich das mit Autofahren vergleichen würde, habe ich vielleicht mal kurz nach vorne und hinten geschaut, was so in meinem Bereich oder meiner Spur an Neuerungen unterwegs sind. In diesem Jahr habe ich das erste Mal die Spur gewechselt und geschaut, was es in anderen Bereichen und Sprachen für neue Konzepte gibt. Und ich muss sagen, das waren so einige!</p>

<p>Das Thema Fortbildung ist nicht nur aber speziell in der IT-Welt sehr wichtig. Die ganze Welt wird immer schnelllebiger und wer sich nicht von Zeit zu Zeit nach Neuem umschaut, verpasst unter Umständen den davonfahrenden Zug. Bezogen auf die Web-Entwicklung gab es über die letzten Jahre viele Entwicklungen die sehr spannend sind. Angefangen bei Sprachkonzepten, über komplett neue Sprachen, wie Swift von Apple oder Kotlin von JetBrains, hin zu breit unterstützten neuen Designtechniken.</p>

<p>In den folgenden Abschnitten möchte ich nun genauer darauf eingehen, wie ich mir diese neuen Themen angeeignet habe und wie auch ihr euch Themen aneignen könnt, die euch entweder beruflich oder aber privat weiterbringen können und ziemlich sicher auch werden.</p>

<h3>News, News, News</h3>

<p>Ich habe vor einigen Jahren damit angefangen, News-Feeds von diversesten Quellen zu abonnieren und die dort veröffentlichten Beiträge zu verfolgen. Dabei habe ich mich nicht auf eine spezielle Richtung festgelegt, sondern viele verschiedene Bereiche berücksichtigt. Gebündelt habe ich das Ganze über den Dienst <a href="https://feedly.com/">Feedly</a>. Für alle wichtigen Plattformen gibt es dafür Apps, entweder von Feedly selbst oder sehr gute Umsetzungen von Drittanbietern, z.B. Nextgen Reader im Windows 10 Store.</p>

<p>In meinem Fall habe ich Seiten aus den folgenden Bereichen abonniert:</p>

<ul>
<li><strong>IT allgemein:</strong> Hier werden allgemeine Themen wie Hardware, Software aber auch der Fachkräftemangel behandelt</li>
<li><strong>Windows und Entwicklung:</strong> Hier werden Neuigkeiten zu Windows und zur App-Entwicklung abgedeckt, auch weil es für mich die Plattform für meine tägliche Arbeit bereitstellt</li>
<li><strong>Web-Entwicklung:</strong> Für mich einer der wichtigsten Bereiche, hier gibt es Neues rund um die Sprachen und Techniken die im Web verwendet werden, z.B. JavaScript, PHP und CSS</li>
<li><strong>Weiteres:</strong> Um das ganze etwas aufzulockern, habe ich auch ein paar Seiten im Feed, die sich mit Inhalten beschäftigen, die ich eher als privates Hobby beschreiben würde</li>
</ul>

<p>Über diese Artikel stoße ich dann oft auf neue Entwicklungen, die ich mir genauer anschauen möchte. Dazu verwende ich sehr häufig eine der nachfolgend beschriebenen Möglichkeiten. Da ich hin und wieder auch neue Sachen aufgreifen und darüber berichten werde, würde ich mich natürlich freuen, wenn ihr meinen Blog in eure Liste aufnehmen wollt. Den Link zum Feed findet ihr im Fußbereich jeder Seite.</p>

<h3>Dokumentationen</h3>

<p>Glücklicherweise befinden wir uns in einer Zeit, in der gute und vor allem ausführliche Dokumentationen zum guten Ton gehören. Da Weiterentwicklungen heute sehr rasant vonstatten gehen, sind diese Dokumentationen meist online verfügbar, wo man schnell auf Änderungen reagieren kann. Es hat sich beispielsweise eingebürgert, dass wenn man etwas für Open Source Projekte entwickelt, gleichzeitig auch die Dokumentation dafür bereitstellt.</p>

<p>Ich habe die Erfahrung gemacht, dass man sich bis zu einem gewissen Grad sehr viel Wissen zu einem Framework oder einer Programmiersprache nur per Lesen der Dokumentation aneignen kann. Wie weit man dabei kommt, hängt dabei häufig von der Strukturierung des Ganzen ab.<br>
Als Beispiel will ich mal die Dokus von <a href="https://docs.phalconphp.com/de/">Phalcon</a> und <a href="https://symfony.com/doc/current">Symfony</a> vergleichen. Beide habe ich mir bereits ausführlich angeschaut und während meiner Projekte verwendet. Da ich vor einigen Jahren bereits mit Phalcon begonnen habe und dort eine sehr gute und strukturierte Dokumentation vorfand, war ich etwas überrascht als ich vor ca. einem Jahr mit Symfony begonnen habe. Dort war zwar der angebotene Inhalt sehr ausführlich, mit vielen Beispielen versehen und sehr gut verständlich. Die Struktur ist aber mehr als gewöhnungsbedürftig. Das liegt situationsbedingt aber auch dran, dass Symfony historisch gesehen eher eine lose Sammlung an Komponenten ist, während Phalcon ein vollständiges Framework ist. Bei Symfony kann man einzelne Komponenten unabhängig vom Rest verwenden, während Phalcon aufgrund des PHP-Moduls immer komplett in den Speicher geladen wird.</p>

<p>Solltet ihr also nächstes Mal auf etwas aufmerksam werden, das ihr vertiefen wollt, werft als erstes einen Blick auf die Dokumentation. Oft wird einem hier schon das wichtigste vermittelt. Solltet ihr dann weiter Interesse haben, ist vielleicht der nächste Punkt für euch zusätzlich interessant.</p>

<h3>Lernplattformen</h3>

<p>Ich bin in diesem Jahr ein großer Fan von Lernplattformen geworden. Bevor ich im Detail darauf eingehe, was geboten wird, möchte ich kurz zwei Vertreter nennen, mit denen ich viel Erfahrung gesammelt habe und die ich empfehlen kann: <a href="https://www.udemy.com/">Udemy</a> und <a href="https://symfonycasts.com/">SymfonyCasts</a>. Die folgenden Zeilen sollen aber keine Werbung sein, sondern lediglich meine Begeisterung zum Ausdruck bringen.</p>

<p>Lernplattformen waren für mich dieses Jahr der Startschuss, um das Thema Fortbildung in Angriff zu nehmen. Ich weiß gar nicht mehr wie ich darauf gekommen bin, ich glaube ein Kollege hat mich darauf gebracht. Seitdem bin ich wie gebannt und verschlinge einen Kurs nach dem anderen. Ich habe dabei auch festgestellt, dass es für bestimmte Sachen besser ist, auch mal den Blickwinkel von Jemandem zu haben, der sich schon lange mit der Thematik beschäftigt.</p>

<p>Alle Plattformen die ich bisher getestet habe, bieten aber auch über das Lernen hinaus weitere hilfreiche Funktionen. Ihr könnt zum Beispiel Fragen stellen, wenn ihr irgendwo nicht weiter kommt. Ihr könnt euch auch an Diskussionen anderer Mitlernender beteiligen und so euer Wissen vertiefen. Viele sog. <em>Dozenten</em> erweitern ihre Kurse auch bei Bedarf wenn es Neuerungen gibt. <em>Dozenten</em> sind in diesem Sinne aber nicht immer Leute mit akademischem Grad. Jeder der möchte kann auch selbst zum Dozenten werden und eigene Kurse anbieten. Ich empfehle euch auch deshalb, die Möglichkeit zu nutzen und euch im Vorfeld eines Kaufs die verfügbaren kostenlosen Vorschau Lektionen anzuschauen. Damit bekommt ihr einen Eindruck vom Dozenten und ob er einen für euch verständlichen Vortragsstil hat. Ich habe tatsächlich den ein oder anderen Kurs nicht gekauft, weil mich der starke Akzent gestört hat und ich mir nicht vorstellen konnte, einen Kurs mit einer länge von 20 Stunden oder länger damit durchzuhalten. Solltet ihr euch dazu entschlossen haben, einen Kurz durchzuarbeiten, solltet ihr auf jeden Fall kursbegleitend auch das gelernte anwenden.</p>

<p>Es besteht natürlich immer die Möglichkeit, sich Videos auch auf YouTube anzusehen. Meine Erfahrung zeigt allerdings, dass hier häufig nur Einführungen zur Verfügung gestellt werden und für weitergehendes Wissen dann auf kostenpflichtige Kurse verwiesen wird. Es ist aber auf jeden Fall einen Blick wert, um sich schnell einen Überblick über bestimmte Themen zu verschaffen. Oft kann man danach beurteilen, ob sich der Aufwand und die Kosten lohnen, tiefer in die Materie einzusteigen.</p>

<p>Nun möchte ich aber doch noch ein paar Worte zu den beiden angesprochenen und von mir getesteten Plattformen los werden.</p>

<h5>Udemy</h5>

<p>Auf dieser Plattform gibt es unzählige Kurse aus allen Bereichen des Lebens. Vor allem als Entwickler findet man hier aber nahezu alles, was das Herz begehrt. Ihr könnt ohne Anmeldung durch die Kurse stöbern und euch umsehen. Sollte euch ein Kurs zusagen, müsst ihr nur noch auf einen der sehr häufigen Aktionsrabatte warten. Die Kurse gibt es für Einzelpersonen auf Abruf. Ich habe bisher nie mehr als 12€ für einen Kurs bezahlt. Wobei ich mir bei vielen Kursen mittlerweile sicher bin, die wären auch den vollen Preis wert. Die vermittelten Inhalte sind oft sehr tiefgreifend und führen zu einem umfangreichen Verständnis der Materie.<br>
Für Firmen gibt es auch ein Flatrate Angebot, wobei ich davon weniger halte. Sowohl der Umfang der verfügbaren Kurse ist deutlich reduziert als auch der abgerufenen Preis ist etwas hoch. Man bekommt dafür eine ganze Menge an Kursen auf Einzelabruf, zumindest wenn man sich bis zur nächsten Rabattaktion gedulden kann.</p>

<p>Da es zu einem Thema oft mehrere Kurse bzw. <em>Dozenten</em> gibt, lohnt sich im Vorfeld eigentlich immer ein Vergleich. Ich persönlich werfe dabei oft einen Blick auf das Inhaltsverzeichnis, vergleiche die Kursdauer und schaue mir die verfügbaren Vorschauvideos an. Die meisten meiner gekauften Kurse haben eine Dauer von zehn Stunden und mehr. Wie bereits erwähnt gibt es zu fast allem was man sich vorstellen kann einen Kurs. Es werden auch sog. Softskills wie Präsentationstechniken, Führungsfertigkeiten oder Vortragsstile behandelt.</p>

<p>Ich schaue sehr häufig die Liste durch und setze dann einige Kurse, die mich interessieren könnten, auf die Wunschliste. Dort habe ich eine schnelle Übersicht der Preise und habe immer genug auf Lager, sollten meine gekauften Kurse mal zur Neige gehen.</p>

<h5>SymfonyCasts</h5>

<p>Wie der Name vermuten lässt, beschäftigt sich diese Plattform mit vielen Themen rund um das Symfony Framework. Lasst euch aber vom Namen nicht täuschen, es gibt dort auch viele andere Kurse rund um die Web-Entwicklung. Vor allem die Einführungskurse für PHP und JavaScript sind zu empfehlen, solltet ihr euch hier Wissen aneignen wollen. Darüber hinaus gibt es auch vereinzelt Kurse aus dem Bereich der Administration und für das Deployment von Anwendungen.</p>

<p>Das Ganze könnt ihr entweder per Einzelabruf oder als Abonnement bezahlen. Ich persönlich habe mich für das Abo entschieden, da ich in zwei Monaten den Großteil der angebotenen Inhalte durcharbeiten konnte und man die Videos für die einzelnen Lektionen auch herunterladen und später noch einmal anschauen kann.</p>

<h3>Konferenzen</h3>

<p>Ich will nur kurz über das Thema Konferenzen sprechen. Es gibt die verschiedensten Arten davon. Häufig befassen sie sich mit Themen rund um Firmen oder Anbieter von Frameworks, die die jeweilige Konferenz ausrichten. Ich persönlich war bisher noch auf keiner Konferenz, da zum einen die Preise teilweise recht happig sind, zum anderen die Themen oft zu breit gefächert sind.</p>

<h5>Seminare</h5>

<p>Ich erwähne diesen Punkt bewusst etwas weiter hinten und auch nur als Unterpunkt. Seminare sind für mich ein Relikt aus vergangener Zeit. Bevor ich für einen Kurs der ein bis zwei Tage dauert viele hundert Kilometer reise und dafür mehrere tausend Euro ausgebe, setzte ich mich lieber vor meinen Bildschirm und gehe in dieser Zeit in meinem eigenen Tempo einen Kurs auf einer der oben genannten Plattformen durch. Zum einen werden dort bestimmte Aspekte oft viel detaillierter behandelt, zum anderen kann ich jederzeit nochmal nachschauen, wenn ich etwas vergessen habe.</p>

<p><strong>Zum Vergleich:</strong> Ich habe bei Udemy aktuell ca. 45 Kurse gekauft und bin preislich weit unter dem, was ein Seminar zu einem einzigen dieser Kurse kosten würde.</p>

<p>Ich möchte hier natürlich Niemanden beleidigen und ich bin mir sicher, dass der Großteil der angebotenen Seminare dieser Welt durchaus eine Daseinsberechtigung hat. Für mich persönlich ist das aber nichts.</p>

<h3>Fazit</h3>

<p>Ich habe euch nun einige Möglichkeiten vorgestellt, wie ihr das Wissen in eurem Bereich oder vielleicht auch darüber hinaus auf dem aktuellen Stand halten und erweitern könnt. Ich würde euch wirklich ans Herz legen, ein paar davon zu versuchen. Solltet ihr einen Punkt vermissen oder andere Fragen haben, könnt ihr mich gerne über das Kontaktformular im Fußbereich kontaktieren und ich werde den Artikel bei Bedarf überarbeiten.</p>

<p>Jetzt will ich euch aber nicht länger aufhalten und wünsche euch viel Spaß beim Lernen!</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Schwank aus dem Leben meines Computers]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/schwank-aus-dem-leben-meines-computers" />
      <id>tag:https:2018:/next-direction.de/2.5</id>
      <published>2018-12-24T11:00:00Z</published>
      <updated>2018-12-27T09:27:09Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/pc.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/pc.jpeg" alt="Teaser - Schwank aus dem Leben meines Computers"/><br>
        <p>Willkommen zurück! Passend zur Weihnachtszeit, will ich heute mal nicht über die neuesten Trends aus der Entwicklung sprechen, sondern einen Blick zurück werfen und erzählen, was ich über die Jahre so an Computern hatte. Aus heutiger Sicht sind die damaligen Kisten geradezu unvorstellbar, sowohl was die Ausstattung anging als auch die Preise, die dafür abgerufen wurden. Alles, was ihr in den folgenden Abschnitten lest, hat mich letztendlich zu dem geformt, der ich heute bin und das hat mich natürlich auch für meine berufliche Entwicklung geprägt. Wie sagt man so schön, es wurde mir in die Wiege gelegt.</p>

<h3>C64</h3>

<p>Ich war glaube ich fünf oder sechs Jahre alt, als die Familie von meinem Cousin einen Commodore 64 bekommen hat. Seht mir bitte nach, dass ich mich nicht mehr daran erinnere, wie viel wir dafür ausgegeben haben. Ich weis nur, dass diese Maschinen teilweise Neupreise von mehreren tausend D-Mark hatten. Ich kann mich aber gut daran erinnern, dass zur Ausstattung auch ein kleiner Röhrenfernseher als Bildschirm, ein 5,25 Zoll Diskettenlaufwerk und zwei Joysticks gehörten. Ich finde leider kein Foto mehr, dass ich hier gefahrlos verwenden kann aber es waren die kleinen schwarzen Joysticks mit zwei roten Knöpfen und einem roten Hebel, Kenner werden sich erinnern. Später haben wir uns noch einen moderneren Joystick zugelegt, um echtes Feeling aufkommen zu lassen, bei den verpixelten Flugsimulatoren der damaligen Zeit.</p>

<h3>Intel 80486</h3>

<p>Zwei bis drei Jahre später ging es dann bergauf. Während ein paar Freunde sich mit einem Intel 80386 rumschlagen mussten, zog bei uns ein Intel 80486 in den Haushalt ein. Damit hatten wir nicht nur ein vielfaches der Leistung - unter anderem 33 MHz Prozessortakt - unseres ersten Computers, sondern auch einen echten Monitor und es gab 3,5 Zoll Diskettenlaufwerke.</p>

<p>Das muss man sich mal vorstellen, hatte man mit den 5,25 Zoll Disketten gerade mal eine Kapazität von 170 kB, verzehnfachte sich der verfügbare Speicher fast, auf 1,44 MB! Spiele wie Doom oder Wolfenstein passten damit auf 7-10 Disketten. Und ich gestehe, diese Disketten habe ich erst vor einem halben Jahr entsorgt. Auch die letzten Diskettenlaufwerke mussten Platz machen für andere Sachen, wer weis ob die überhaupt noch funktioniert haben.</p>

<p>Zu dieser Zeit bin ich dann auch das erste mal mit einem Windows Betriebssystem in Berührung gekommen - Windows 3.11. Überwiegend schwarz-weiß war das Wort <em>Maus</em> noch nicht bekannt. Man bediente das System hauptsächlich mit der Tastatur und Spiele wurden weiterhin mit Tastatur und Joystick gespielt.</p>

<h3>Intel Pentium</h3>

<p>Danach wurde es richtig modern. Wir haben wieder eine Generation - den Pentium 1 - übersprungen und bekamen direkt den Pentium 2 mit unglaublichen 400 MHz. Damit konnte man schon richtig was anfangen. Das war auch die Zeit, in der CD-Laufwerke in Mode kamen und die guten alten Shareware Spiele-CDs in die Läden kamen. Tausende von Spielen auf einer einzigen Scheibe, was für ein Fortschritt!</p>

<p>Wenn man versuchte alte Spiele vom 80486 auf dem neuen PC zu spielen, kam man mit dem Schauen kaum hinterher, weil die Bildrate an die Taktrate gebunden war und die Spiele damit bis zu zehn Mal so schnell abgelaufen sind.</p>

<p>Auch die Betriebssysteme wurden moderner und Windows 95 schlich sich in mein Leben. Später wurde es von Windows 98 abgelöst. Und auch Mäuse wurden nun nicht mehr ausschließlich mit Fallen erledigt, sondern auch zur Bedienung von PCs verwendet.</p>

<p>Bevor wir im nächsten Abschnitt dann zu meinem ersten wirklich eigenen Computer kommen, gab es noch einen Zwischenschritt. Wir haben uns einen Pentium 3 mit 1 GHz zugelegt. Im wesentlichen blieb damit zwar alles beim alten, aber Spiele liefen nochmal etwas flüssiger und man konnte auch höhere Auflösungen einstellen.</p>

<h3>Neuzeit</h3>

<p>Kommen wir nun also zum ersten eigenen Computer. Das war so die Zeit, als Windows 2000 veröffentlicht wurde. Da ich schon immer auf ein gesundes Preis-/Leistungsverhältnis geachtet habe, war es damals ein System mit AMD Athlon Prozessor. Da ich bis dahin nie einen PC von innen gesehen habe, war es ein Komplettsystem, das zusammengebaut bei mir ankam. Durch meine Ausbildung habe ich dann das nötige Wissen gesammelt, um von da an die nachfolgenden Kisten selbst zusammenbauen zu können.</p>

<p>Über die Jahre gab es dann ein paar Hardware Austauschaktionen und auch die Betriebssysteme wurden dazu passend ausgewählt. Nach Windows XP folgte bei mir direkt Windows 7. Den Ausflug in die wunderbare Welt von Windows Vista habe ich mir gespart, auch weil ich von vielen Bekannten, die sich Komplettsysteme damit gekauft hatten, nur negative Berichte mitbekommen habe. Anders als viele andere, war ich bei Windows 8 von Anfang an dabei und war auch wirklich begeistert von diesem System. Es war eine neue Art der Menüführung und es lief sehr gut auch auf Tablets.</p>

<p>Bis auf eine Ausnahme waren meine Systeme immer AMD basiert. Nur als vor ein paar Jahren die Leistung von AMD meilenweit hinter der von Intel lag, habe ich mich dazu durchgerungen, einen etwas teureren Core i5 Prozessor und dazu passend natürlich ein Mainboard und den Arbeitsspeicher zu kaufen. Natürlich bin ich mittlerweile auch zu Windows 10 gewechselt und war auch von Beginn an im Windows Insider Programm. Das habe ich aber mittlerweile verlassen, da mir hier zu häufig komplette Neuinstallationen des System notwendig waren.</p>

<p>Kurz bevor ich diesen Blog startete, habe ich mein System noch einmal runderneuert. Es basiert jetzt auf einem AMD Ryzen 7 2700X. Dazu gab es auch 16GB Arbeitsspeicher, um die ganzen Virtualisierungen - inklusive Docker auf Hyper-V - flüssig laufen zu lassen, die man benötigt, um diverse Technologien auszuprobieren. Ich denke damit bin ich die nächsten Jahre wieder gut aufgestellt, um euch auch weiterhin mit aufheiternden Beiträgen versorgen zu können.</p>

<h3>Ausblick</h3>

<p>Wenn man sich die Entwicklung der letzten Jahrzehnte anschaut, dann macht das Lust auf die nächsten Jahrzehnte. Ich denke wir sind nicht mehr all zuweit entfernt von Sci-Fi Szenarien, die so vor 20 Jahren durch Filmemacher ersponnen worden sind. Ich hoffe, ihr seid alle mit dabei und freut euch genauso darauf wie ich.</p>

<p>Nun will ich euch aber nicht länger aufhalten. Wie immer hoffe ich auf regen Besuch, wenn die nächsten Beiträge veröffentlicht werden.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[SEO mit ExpressionEngine]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/seo-mit-expressionengine" />
      <id>tag:https:2018:/next-direction.de/2.12</id>
      <published>2018-12-22T13:00:00Z</published>
      <updated>2018-12-27T09:27:14Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/search.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/search.jpeg" alt="Teaser - SEO mit Expression"/><br>
        <p>Willkommen zurück! Aus gegebenem Anlass will ich heute einen Beitrag einschieben, in dem ich euch an meinen <abbr title="Search Engine Optimization (Suchmaschinenoptimierung)">SEO</abbr> Maßnahmen teilhaben lasse. Ich habe mich die letzten Tage etwas in die Materie eingearbeitet und will meine Erfahrungen nun mit euch teilen. Die Umsetzung ist dabei nicht ausschließlich an ExpressionEngine gebunden. Viele der vorgestellten Maßnahmen gelten allgemein, die Anwendung mit ExpressionEngine ist aber teilweise etwas verzwickt.</p>

<dl>
<dt><abbr title="Search Engine Optimization (Suchmaschinenoptimierung)">SEO</abbr> kurz und knapp</dt>
<dd>Was ist nun <abbr title="Search Engine Optimization (Suchmaschinenoptimierung)">SEO</abbr>? Unter <abbr title="Search Engine Optimization (Suchmaschinenoptimierung)">SEO</abbr> versteht man allgemein die Optimierung bestimmter Eigenschaften von Webseiten, um es Suchmaschinen zu erleichtern, den eigenen Inhalt zu finden und zu verstehen. In den meisten Fällen passiert all das, ohne dass die Leser bzw. Benutzer einer Seite etwas davon mitbekommen. Es ist also ein wenig Arbeit, die man nicht direkt sehen kann, die sich aber lohnt wenn ihr mehr Besucher auf die Seite lotsen wollt.</dd>
</dl>

<p>In den folgenden Abschnitten stelle ich nun die einzelnen Maßnahmen genauer vor. Einige davon kann auch ein Laie problemlos umsetzen, andere wiederum erfordern etwas technisches Verständnis. Ich versuche mein Bestes, um auch die komplizierteren Teile verständlich zu erklären und mit Beispiele zu versehen. Um den Beitrag aber nicht zu sehr in die Länge zu ziehen, setze ich ein Grundwissen zur Arbeit mit ExpressionEngine voraus.</p>

<h3>Überschriften</h3>

<p>Fast schon eine Selbstverständlichkeit ist die Verwendung von Überschriften. Dabei solltet ihr auch die Ebenen der Überschriften beachten. Während ihr für den jeweiligen Titel einer Seite am besten <code>h1</code> verwendet, eignen sich für Zwischenüberschriften dann eher <code>h2</code> - <code>h6</code>, je nachdem wie tief ihr verschachteln wollt. Ich habe mir bei den Beiträgen angewöhnt nur <code>h3</code> und <code>h5</code> für die (Unter-)Abschnitte zu verwenden. Für Suchmaschinen wichtig ist nur, dass man nur einmal <code>h1</code> verwendet, weil darin das Thema der Seite vermutet wird. Solltet ihr also für jeden Abschnitt die höchste Ebene der Überschrift verwenden, kann das Suchmaschinen verwirren und das mögen sie nicht wirklich, was in einer schlechteren Einstufung der Seite endet.</p>

<h3>Titel und Meta Tags</h3>

<p>Auch eine seit vielen Jahren bekannte und angewendete Maßnahme zur Optimierung der Suchergebnisse eurer Seite sind sog. Meta-Tags und der Titel eurer Seite. Die HTML-Tags dafür befinden sich üblicherweise im <code>head</code> bereich eures HTML-Markups und sind für Besucher nicht zu sehen. Suchmaschinen wiederum werten diese Informationen aus, um eure Seite einschätzen zu können.</p>

<p>Hier die wichtigsten Tags am Beispiel von Next Direction:</p>

<pre><code class="html">&lt;title&gt;Next Direction&lt;/title&gt;

&lt;meta charset="utf-8"&gt;
&lt;meta http-equiv="content-Language" content="de" /&gt;
&lt;meta name="publisher" content="Next Direction" /&gt;
&lt;meta http-equiv="Reply-to" content="info@next-direction.de" /&gt;
&lt;meta name="revisit-after" content="2 days" /&gt;
&lt;meta name="description" content="Auf Next Direction werden aktuelle Themen..." /&gt;
&lt;meta name="keywords" content="blog, web, development, web, tech, nodejs, php" /&gt;
</code></pre>

<p>Diese Infos befinden sich bei mir auf jeder Seite. Bestimmte Inhalte können über ExpressionEngine <a href="https://docs.expressionengine.com/latest/templates/globals/single_variables.html">Template Variablen</a> ausgelesen werden, damit sie sich einfach und zentral ändern lassen. Ein Beispiel dafür ist der Titel der Seite. Diesen können wir auch mit <code>&#123;site_name&#125;</code> direkt aus den Einstellungen übernehmen.</p>

<h5>Dynamische Seitentitel</h5>

<p>Es ist üblich, auf Unterseiten bzw. in meinem Fall für die einzelnen Beiträge, Seitentitel zu verwenden, die auf den Inhalt der Seite schließen lassen. Wie können wir also den Seitentitel aus ExpressionEngine in das <code>&lt;title&gt;&lt;/title&gt;</code> Tag unseres Templates einfügen?</p>

<p>Dazu muss ich kurz etwas ausholen und den groben Aufbau meiner Seite beschreiben. Weil ich natürlich nicht für jede einzelne Seite immer wieder die grundlegende Struktur der Seite wiederholen möchte, gibt es dafür ein <a href="https://docs.expressionengine.com/latest/templates/layouts.html">Layout</a>. Das Layout beinhaltet die oben angesprochenen Meta-Tags und die nötigen CSS Definitionen und JavaScript Funktionen, die auf allen Seiten benötigt werden. Die Seiten meines Blogs erweitern dann dieses Layout und fügen den jeweiligen Inhalt ein, z.B. den Inhalt des Beitrags, den ihr gerade lest. ExpressionEngine bietet eine <a href="https://docs.expressionengine.com/latest/templates/layouts.html#layout-variables">elegante Möglichkeit</a> namens Layoutvariablen, um Informationen von diesen Seiten an das Layout weiterzureichen und dort zu verwenden.</p>

<p>Wir wollen also den Titel eines Beitrags im Titel der Seite verwenden, um ihn in Suchergebnissen entsprechend wiederzufinden. Dazu müssen wir zuerst das oben gezeigte Tag für den Titel etwas abändern:</p>

<pre><code class="html">&lt;title&gt;&#123;if layout:page_title != ''&#125;&#123;layout:page_title&#125; | &#123;/if&#125;&#123;site_name&#125;&lt;/title&gt;
</code></pre>

<p>Zum Abschluss dieses Abschnitts fehlt also noch das Setzen der Information auf der Seite, die den Einzelbeitrag anzeigt. ExpressionEngine unterscheidet standardmäßig nicht zwischen der Anzeige eines einzelnen Beitrags oder aller Beiträge. Ich habe mich jedoch dazu entschieden, für beides jeweils ein eigenes Template zu erstellen, da es mir mehr Flexibilität bei der Anzeige bietet. Hier der Ausschnitt meines Templates zur Anzeige des Beitrages, indem auch dynamisch der Titel des Beitrags an das Layout weitergegeben wird:</p>

<pre><code class="html">&#123;exp:channel:entries channel='posts' limit='1' require_entry='yes'&#125;
    &lt;article&gt;
        &lt;!-- Markup für Beitrag --&gt;
    &lt;/article&gt;
    &#123;layout:set name="page_title"&#125;&#123;title&#125;&#123;/layout:set&#125;
&#123;/exp:channel:entries&#125;
</code></pre>

<p>Geschafft! Jeder einzelne Beitrag hat nun einen individuellen Titel und taucht damit unterscheidbar für eure Besucher in den Suchergebnissen auf.</p>

<h3>Sprechende URLs</h3>

<p>Immer wieder Thema wenn es um die Optimierung eurer Seite für Suchmaschinen geht, sind sprechende URLs. Bei vielen <abbr title="Content Management System">CMS</abbr>-Systemen braucht man hier Plugins und Erweiterungen. Auch bei Typo3 hat es diese Funktion erst mit Version 9 in den Standard geschafft. ExpressionEngine bietet diese Funktion direkt und ohne Umwege an. Jeder Beitrag den ich erstelle bekommt automatisch einen URL Titel zugewiesen, den ich bei Bedarf auch ändern kann. In der Regel handelt es sich dabei um den Titel des Beitrags, bei dem alle Sonderzeichen entfernt werden und nur Zeichen übrig bleiben, die in einer URL unbedenklich sind. Sprechende URLs sind wichtig, da sie in den Suchergebnissen meist sehr prominent direkt unterhalb des Titels angezeigt werden. Da lese ich lieber</p>

<pre><code class="html">https://next-direction.de/posts/seo-mit-expressionengine
</code></pre>

<p>als etwas kryptisches wie</p>

<pre><code class="html">https://next-direction.de/posts/?id=100
</code></pre>

<p>Wie ihr solche Links erzeugt? Ganz einfach! ExpressionEngine bietet eine Funktion, um einen Link zu einem Einzelbeitrag zu erzeugen. Hier wieder ein kleiner Ausschnitt aus meinem Template zur Anzeige der Liste von Beiträgen, in dem ihr sehen könnt, wie der Link erzeugt wird:</p>

<pre><code class="html">&lt;section class="article-list"&gt;
    &#123;exp:channel:entries channel="posts" limit="10"&#125;
        &lt;article&gt;
            &lt;div class="article-image"&gt;
                &lt;a href="&#123;route='posts/single-post' url_title='&#123;url_title&#125;'&#125;"&gt;
                    &lt;!-- Bild des Beitrags --&gt;
                &lt;/a&gt;
            &lt;/div&gt;
            &lt;div class="article-content"&gt;
                &lt;h4&gt;&lt;a href="&#123;route='posts/single-post' url_title='&#123;url_title&#125;'&#125;"&gt;&#123;title&#125;&lt;/a&gt;&lt;/h4&gt;
                &lt;!-- Kurzer Ausschnitt des Beitrags --&gt;
                &lt;div class="continue-reading"&gt;
                    &lt;a href="&#123;route='posts/single-post' url_title='&#123;url_title&#125;'&#125;"&gt;weiterlesen&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/article&gt;
    &#123;/exp:channel:entries&#125;
&lt;/section&gt;
</code></pre>

<p>Der entscheidende Teil ist der hier:</p>

<pre><code class="html">&lt;a href="&#123;route='posts/single-post' url_title='&#123;url_title&#125;'&#125;"&gt;
</code></pre>

<p>Mit der <code>&#123;route=""&#125;</code> Funktion erzeugt ihr einen Link zur angegebenen Seite und darin wird der entsprechende URL Titel direkt aus dem Beitrag verwendet.</p>

<h3>Bilder mit &#8220;alt&#8221; Attribut</h3>

<p>Ein weiterer Punkt, der relativ leicht umzusetzen ist, sind <em>alt</em> Attribute für Bilder. Diese Attribute sind wichtig für Programme wie Screen Reader, die einem den Inhalt der Seite vorlesen. Die Abkürzung <em>alt</em> steht dabei für <em>alternativ</em> und wird immer dann wichtig, wenn jemand das Bild nicht wahrnehmen kann oder es einfach nicht vom Server geladen werden kann. Suchmaschinen legen großen Wert darauf, dass diese Information verfügbar ist.</p>

<p>Hier ein Codeschnipsel aus meinen Templates, um den Titel des hochgeladenen Bildes als alternativen Text zu verwenden:</p>

<pre><code class="html">&lt;img src="&#123;image&#125;" alt="&#123;image&#125;&#123;title&#125;&#123;/image&#125;"&gt;
</code></pre>

<p>Das über ExpressionEngine angelegte Feld für das Bild heißt in diesem Fall <code>image</code>. Ihr könnt dem Feld aber auch einen beliebigen anderen Namen geben. Man sieht auch den Unterschied in der Verwendung. Das ist ein gebräuchliches Muster in ExpressionEngine. Manche Platzhalter können sowohl in der einfachen Variante <code>&#123;image&#125;</code> als auch in der erweiterten Variante <code>&#123;image&#125;...&#123;/image&#125;</code> verwendet werden. Mit der erweiterten Variante könnt ihr dann auf Meta-Informationen des Bildes zugreifen.</p>

<h3>Mobile Welten</h3>

<p>In der heutigen Zeit, ist eine der wichtigsten Eigenschaften eurer Seite das Verhalten auf mobilen Geräten bzw. auf kleinen Bildschirmen. Ich habe es mir angewöhnt, wenn ich mit dem Design einer Seite beginne, zuerst auf die kleinen Bildschirme zu optimieren und im Anschluss über CSS <code>@media</code> Queries kleinere Anpassungen für die Desktop Browser vorzunehmen. Ich empfehle in diesem Fall immer mit <code>min-width</code> zu Arbeiten.</p>

<p>Auf manchen Seiten reicht es, bestimmte maximale Breiten oder Höhen festzulegen. Oft kann man auch die Anordnung von Elementen über <code>grid</code> bzw. <code>flex</code> ändern um ein Layout zu erhalten, das auf kleine bzw. große Bildschirme ausgerichtet ist.</p>

<p>Da dieser Beitrag aber keine Einführung in das Design von responsiven Webseiten sein soll, belasse ich es an dieser Stelle mit der kurzen Einführung.</p>

<h3>Wer ist dieser <abbr title="JavaScript Object Notation for Linking Data">JSON-LD</abbr></h3>

<p>Wer in der Welt der Entwicklung unterwegs ist, wird über kurz oder lang auf das <abbr title="JavaScript Object Notation">JSON</abbr> Format stoßen. Es ist ein sehr leichtgewichtiges Format, um Daten zwischen Systemen auszutauschen. Auch für den Menschen ist es einigermaßen leicht zu verstehen, wodurch es in den letzten Jahren quasi zum Standard geworden ist, der von fast allen Programmiersprachen unterstützt wird.</p>

<p><abbr title="JavaScript Object Notation for Linking Data">JSON-LD</abbr> ist eine Erweiterung davon und dient zur Bereitstellung von strukturierten Daten, die den Inhalt einer Seite näher beschreiben. Seit einigen Jahren gibt es auch eine Empfehlung des <a href="https://www.w3.org/Consortium/"><abbr title="World Wide Web Consortium">W3C</abbr></a>, den eigenen Seiteninhalt damit zu beschreiben. Obwohl Suchmaschinen über die Jahre immer intelligenter geworden sind und viele der gefundenen Inhalte richtig einsortiert haben, ist es nochmal was anderes, wenn der Seitenbetreiber exakte Informationen über den Inhalt bereitstellt.</p>

<p>Vielleicht habt ihr schon bemerkt, dass Google über die letzten Jahre für immer mehr gesuchte Inhalte spezielle Boxen am rechten Rand anzeigt.</p>

<p><img src="https://next-direction.de/images/uploads/googleInfoBox.jpg" alt="Anzeige strukturierter Daten" /></p>

<p>Diese Informationen könnt auch ihr für eure Seite bereitstellen. Die dazu verwendete Auszeichnungssprache heißt, genau, ihr habt es erraten, <abbr title="JavaScript Object Notation for Linking Data">JSON-LD</abbr>. Ihr müsst euch aber nicht komplett selbst überlegen, wie ihr diese Information aufzubauen habt. Es gibt ein Projekt namens <a href="https://schema.org/">schema.org</a>, das eine riesige Menge an möglichen Inhaltstypen beschreibt. In meinem Beispiel bieten sich die beiden Typen <em>Blog</em> und <em>BlogPosting</em> an. Es gibt aber auch Schemata für Personen oder Kochrezepte. Am einfachsten verwendet ihr eine Suchmaschine eures Vertrauens und sucht nach Inhaltselementen, die auf eurer Seite zu finden sind. Ich bin mir sicher, euer Schema ist auch bereits definiert.</p>

<dl>
<dt>Tipp</dt>
<dd>Google bietet eine Seite an, mit der ihr die strukturierten Daten eurer Webseiten testen könnt. Das Ganze findet ihr <a href="https://search.google.com/structured-data/testing-tool?hl=de">hier</a>.</dd>
</dl>

<p><br></p>

<h5><abbr title="JavaScript Object Notation for Linking Data">JSON-LD</abbr> und ExpressionEngine</h5>

<p>Die Verwendung von strukturierten Daten in ExpressionEngine ist sicherlich das komplexeste Thema, das ich in diesem Beitrag behandle. Es gibt zwar eine Erweiterung, die sich mit der Thematik beschäftigt, allerdings hat mir die nach meinen Tests nicht gefallen und ich habe mich dazu entschieden das Ganze mit Bordmitteln umzusetzen. Ich bin auch ein Verfechter davon, so wenig wie möglich Abhängigkeiten in einem Projekt zu verwenden.</p>

<p>Zur Umsetzung verwenden wir eine Funktion, die ich bereits vorgestellt habe, die Templatevariablen. In dem folgenden Beispiel beschreibe ich mein Vorgehen, um meinen Blog und die Beiträge auf der Startseite zu beschreiben. Da sich die Beiträge ändern können, werden diese Informationen dynamisch an das Template weitergegeben und dort entsprechend aufbereitet.</p>

<p>Hier nun als erstes der Teil, der die Informationen an das Layout weiterreicht:</p>

<pre><code class="html">&lt;section class="articles"&gt;
    &#123;exp:channel:entries channel="posts" limit="4"&#125;
        &lt;!-- Hier das Markup für die Darstellung der Beiträge --&gt;
        &#123;layout:set:append name="json_list_title"&#125;&#123;title&#125;&#123;/layout:set:append&#125;
        &#123;layout:set:append name="json_list_image"&#125;&#123;image&#125;&#123;/layout:set:append&#125;
        &#123;layout:set:append name="json_list_short_text"&#125;&#123;short_text&#125;&#123;/layout:set:append&#125;
        &#123;layout:set:append name="json_list_full_text"&#125;&#123;full_text&#125;&#123;/layout:set:append&#125;
        &#123;layout:set:append name="json_list_author"&#125;&#123;author&#125;&#123;/layout:set:append&#125;
        &#123;layout:set:append name="json_list_keywords"&#125;&#123;keywords&#125;&#123;/layout:set:append&#125;
        &#123;layout:set:append name="json_list_url_title"&#125;&#123;url_title&#125;&#123;/layout:set:append&#125;
        &#123;layout:set:append name="json_list_published"&#125;&#123;entry_date format="%Y-%m-%dT%H:%i:00+00:00"&#125;&#123;/layout:set:append&#125;
        &#123;layout:set:append name="json_list_edited"&#125;&#123;edit_date format="%Y-%m-%dT%H:%i:00+00:00"&#125;&#123;/layout:set:append&#125;
    &#123;/exp:channel:entries&#125;
&lt;/section&gt;
</code></pre>

<p>Im Gegensatz zur Syntax, die ich bei den Seitentiteln verwendet habe, kommt hier eine leichte Abwandlung zum Einsatz <code>layout:set:append</code>. Diese sorgt dafür, dass kein einzelner Wert sondern ein Array, also eine Liste von Werten, übergeben werden kann. Das ist notwendig, da ich für jeden Beitrag der Startseite eine eigene Beschreibung erstellen will.</p>

<p>Es ist üblich, diese Informationen, wie die Meta-Tags auch, im <code>head</code> Bereich der Seite unterzubringen. Deshalb habe ich das Ganze über Bedingungen in mein Layout eingefügt. Nur wenn die angezeigte Seite Informationen bereitstellt, wird über das Layout auch die <abbr title="JavaScript Object Notation for Linking Data">JSON-LD</abbr> Beschreibung dazu erzeugt. Der Code, der für die Erzeugung zuständig ist, sieht folgendermaßen aus:</p>

<pre><code class="json">&#123;
    "@context": "http://schema.org",
    "@type": "Blog",
    "name": "&#123;site_name&#125;",
    "url": "&#123;site_url&#125;",
    "description": "Auf Next Direction werden aktuelle Themen rund um die Web-Entwicklung vorgestellt. Hin und wieder werden auch Themen aus verwandten Gebieten aufgegriffen.",
    "sameAs": &#91;
        "https://twitter.com/NextDirectionDE",
        "https://github.com/next-direction"
    &#93;,
    "publisher": &#123;
        "@type": "Organization",
        "name": "&#123;site_name&#125;",
        "logo": &#123;
            "@type": "ImageObject",
            "name": "NextDirectionLogo",
            "width": "512",
            "url": "&#123;site_url&#125;images/Logo512.png"
        &#125;
    &#125;
    &#123;if layout:json_list_title&#125;
    ,
    "blogPost": &#91;
        &#123;layout:json_list_title&#125;
            &#123; 
                "@context": "http://schema.org", 
                "@type": "BlogPosting",
                "headline": "&#123;exp:nd_util:htmlentities&#125;&#123;value&#125;&#123;/exp:nd_util:htmlentities&#125;",
                "image": "&#123;layout:json_list_image index='&#123;index&#125;'&#125;",
                "keywords": "&#123;layout:json_list_keywords index='&#123;index&#125;'&#125;", 
                "publisher": &#123;
                    "@type": "Organization",
                    "name": "&#123;site_name&#125;",
                    "logo": &#123;
                        "@type": "ImageObject",
                        "name": "NextDirectionLogo",
                        "width": "512",
                        "url": "&#123;site_url&#125;images/Logo512.png"
                    &#125;
                &#125;,
                "url": "&#123;path='/posts'&#125;/&#123;layout:json_list_url_title index='&#123;index&#125;'&#125;",
                "mainEntityOfPage": "&#123;path='/posts'&#125;/&#123;layout:json_list_url_title index='&#123;index&#125;'&#125;",
                "datePublished": "&#123;layout:json_list_published index='&#123;index&#125;'&#125;",
                "dateModified": "&#123;layout:json_list_edited index='&#123;index&#125;'&#125;",
                "description": "&#123;exp:nd_util:htmlentities&#125;&#123;layout:json_list_short_text index='&#123;index&#125;'&#125;&#123;/exp:nd_util:htmlentities&#125;",
                "author": &#123;
                    "@type": "Person",
                    "name": "&#123;layout:json_list_author index='&#123;index&#125;'&#125;"
                &#125;
                &#125;
                &#123;if count != total_results&#125;
                ,
                &#123;/if&#125;
        &#123;/layout:json_list_title&#125;
    &#93;
    &#123;/if&#125;
&#125;
</code></pre>

<p>Das Ganze solltet ihr in ein <code>&lt;script type="application/ld+json"&gt;&lt;/script&gt;</code> Tag fassen.</p>

<p>Puh, ganz schön viel auf einmal oder? Ich möchte an dieser Stelle nicht zu sehr ins Detail von <em><abbr title="JavaScript Object Notation for Linking Data">JSON-LD</abbr></em> und <em>schema.org</em> einsteigen, sondern nur die Teile beschreiben, die sich auf ExpressionEngine beziehen. Ich habe versucht, soviel wie möglich aus den Einstellungen zu laden, z.B. den Titel der Seite oder auch die URL zu Next Direction.</p>

<p>Der interessante Teil beginnt bei <code>&#123;if layout:json_list_title&#125;</code>. Wenn eine Seite diese Informationen an das Layout übergibt, wird für jeden übergebenen Beitrag eine <abbr title="JavaScript Object Notation for Linking Data">JSON-LD</abbr> Beschreibung mit dem <em>BlogPosting</em> Schema erstellt. Dafür ist der folgende Ausschnitt verantwortlich, der so oft durchlaufen wird, wie es Beiträge auf der Startseite gibt:</p>

<pre><code class="html">&#123;layout:json_list_title&#125;
    &lt;!-- BlogPosting Markup --&gt;
&#123;/layout:json_list_title&#125;
</code></pre>

<p>Zum Abschluss dieses Abschnitts möchte ich noch kurz auf zwei Besonderheiten eingehen</p>

<ul>
<li><code>&#123;exp:nd_util:htmlentities&#125;&#123;value&#125;&#123;/exp:nd_util:htmlentities&#125;</code>: Hier wird der Titel des Inhalts ausgegeben. Da es ein Problem gab, wenn in einem Text ein Anführungszeichen vorkam, musste ich hier ein eigenes Plugin schreiben, welches diese Passagen entschärft. Solltet ihr Interesse daran haben, könnt ihr mich gerne kontaktieren.</li>
<li><code>&#123;layout:json_list_keywords index='&#123;index&#125;'&#125;</code>: Über das obige Markup, welches die Beiträge durchläuft, wird nur die Liste der Titel durchlaufen. Um dann im jeweiligen Durchlauf auf die weiteren Elemente des Beitrags zuzugreifen, gibt es den Platzhalter <code>&#123;index&#125;</code>. Der beinhaltet immer die Nummer des aktuellen Beitrags und erlaubt somit den Zugriff auf die zugehörigen Eigenschaften des Beitrags, in diesem Fall die Schlüsselwörter.</li>
</ul>

<h3>Sitemap</h3>

<p>Der letzte Aspekt, den ich euch noch kurz vorstellen will, ist die Sitemap. Damit teilt ihr einer Suchmaschine mit, welche Seiten es bei euch gibt. Google bietet eine relativ einfache Möglichkeit über die <a href="https://search.google.com/search-console/about?hl=de">Search Console</a>, die Sitemap zu registrieren. Damit erleichtert ihr es einer Suchmaschine, bestimmte Seiten zu finden und in den Index aufzunehmen.</p>

<p>Für meinen Blog sieht das Template zur Erstellung einer solchen Sitemap wie folgt aus:</p>

<pre><code class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;urlset
  xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
  http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"&gt;
    &lt;url&gt;
      &lt;loc&gt;&#123;site_url&#125;&lt;/loc&gt;
      &lt;lastmod&gt;&#123;current_time format="%Y-%m-%dT%H:%i:00+00:00"&#125;&lt;/lastmod&gt;
      &lt;changefreq&gt;weekly&lt;/changefreq&gt;
    &lt;/url&gt;
    &#123;exp:channel:entries channel="posts"&#125;
      &lt;url&gt;
        &lt;loc&gt;&#123;path='/posts'&#125;/&#123;url_title&#125;&lt;/loc&gt;
        &lt;lastmod&gt;&#123;entry_date format="%Y-%m-%dT%H:%i:00+00:00"&#125;&lt;/lastmod&gt;
        &lt;changefreq&gt;weekly&lt;/changefreq&gt;
      &lt;/url&gt;
    &#123;/exp:channel:entries&#125;
    &lt;url&gt;
      &lt;loc&gt;&#123;path='/posts/all-posts'&#125;&lt;/loc&gt;
      &lt;lastmod&gt;2018-12-20T06:31:54+00:00&lt;/lastmod&gt;
      &lt;changefreq&gt;weekly&lt;/changefreq&gt;
    &lt;/url&gt;
    &#123;exp:channel:entries channel="site" entry_id="1|2"&#125;
        &lt;url&gt;
          &lt;loc&gt;&#123;path='/site'&#125;/&#123;url_title&#125;&lt;/loc&gt;
          &lt;lastmod&gt;&#123;edit_date format="%Y-%m-%dT%H:%i:00+00:00"&#125;&lt;/lastmod&gt;
          &lt;changefreq&gt;monthly&lt;/changefreq&gt;
        &lt;/url&gt;
    &#123;/exp:channel:entries&#125;
    &lt;url&gt;
      &lt;loc&gt;&#123;path='/rss'&#125;&lt;/loc&gt;
      &lt;lastmod&gt;2018-12-20T06:21:25+00:00&lt;/lastmod&gt;
      &lt;changefreq&gt;weekly&lt;/changefreq&gt;
    &lt;/url&gt;
    &lt;url&gt;
      &lt;loc&gt;&#123;path='/site/contact'&#125;&lt;/loc&gt;
      &lt;lastmod&gt;2018-12-20T06:31:55+00:00&lt;/lastmod&gt;
      &lt;changefreq&gt;monthly&lt;/changefreq&gt;
    &lt;/url&gt;
&lt;/urlset&gt;
</code></pre>

<p>Im Grunde werden alle Seiten, die für die Öffentlichkeit relevant sind, aufgelistet. Es werden bestimmte Informationen angegeben, wie der Zeitpunkt der letzten Bearbeitung oder aber die Frequenz mit der sich Inhalte ändern können.</p>

<p>Bei statischen Seiten, wie dem Kontaktformular, reicht es wenn man als Änderungsfrequenz <em>monatlich</em> übergibt. Bei dynamischen Inhalten wie den Beiträgen selbst, habe ich mich dazu entschieden auf <em>wöchentliche</em> Änderungen zu setzen. Diese Informationen helfen einer Suchmaschine beurteilen zu können, wie oft sie auf den gelisteten Seiten vorbeischauen soll.</p>

<h3>Fazit</h3>

<p>Wie immer, hier noch eine kurze Zusammenfassung der Thematik. Wenn ihr euren Webauftritt seriös gestalten möchtet, solltet ihr auf jeden Fall tiefer in das Thema <abbr title="Search Engine Optimization (Suchmaschinenoptimierung)">SEO</abbr> einsteigen. Für Firmen ist es auf jeden Fall eine Überlegung wert, sich an einen Experten zu wenden. Es gibt viele Feinheiten, die nicht immer irgendwo dokumentiert sind und jemand der sich jeden Tag mit der Thematik beschäftigt, wird hier auf jeden Fall noch mehr für euch rausholen. Für eher kleinere oder privatere Seiten, reicht es meiner Meinung nach, sich ein paar Stunden einzulesen und die wichtigsten Punkte, die ich beschrieben habe, umzusetzen.</p>

<p>Jetzt will ich euch aber nicht länger aufhalten. Hoffentlich bis zum nächsten Mal!</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Open Source: Eine Erfolgsgeschichte]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/open-source-eine-erfolgsgeschichte" />
      <id>tag:https:2018:/next-direction.de/2.4</id>
      <published>2018-12-16T19:05:00Z</published>
      <updated>2019-03-23T16:11:35Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/code.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/code.jpeg" alt="Teaser - Open Source: Eine Erfolgsgeschichte"/><br>
        <p>Willkommen zurück! Bevor wir in ein paar Beiträgen mit den richtig technischen Themen beginnen, will ich noch ein paar allgemeine Themen behandeln. Das erste davon ist das Thema <em>Open Source</em>. Natürlich nicht ganz ohne Hintergedanken, denn viele Beiträge auf Next Direction werden in irgendeiner Weise damit zu tun haben. Ich werde immer wieder Bibliotheken und Frameworks vorstellen, die aus der Open Source Szene stammen. Auch für die Entwicklung eigener Projekte, die ich vorstellen möchte, verwende ich oft frei verfügbare Programme.</p>

<dl>
<dt>Definition Open Source</dt>
<dd>Unter Open Source Software verstehe ich Software, deren Quellcode von jedem eingesehen werden kann. Sehr oft wird dieser auf Plattformen wie GitHub oder GitLab veröffentlicht. Mit Open Source verbinden viele auch das Wort <em>kostenlos</em>, was in den meisten Fällen natürlich uneingeschränkt gilt.</dd>
</dl>

<p>Die nächsten Abschnitte möchte ich dazu nutzen, verschiedene Aspekte der Open Source Szene zu beleuchten und auch Vor- und Nachteile davon möchte ich euch natürlich nicht verschweigen.</p>

<h3>Rückblick</h3>

<p>Gerade in der heutigen Zeit nimmt Open Source Software einen hohen Stellenwert ein. Es gibt eine große Anzahl hochqualitativer Software, die frei und in diesem Fall auch kostenlos verfügbar ist. Wenn man zurück in die Vergangenheit blickt, dann ist für mich das prominenteste Beispiel nach wie vor Linux. Ein Betriebssystem, das von Anfang an mit dem Gedanken der Quelloffenheit entwickelt wurde und bis Heute wird.<br>
Unter dem Begriff Linux verstehen unterschiedliche Leute oft unterschiedliche Dinge. Für mich ist Linux im wesentlichen der Kern des Betriebssystems welcher gemeinhin auch als <em>Kernel</em> bezeichnet wird. Auf diesem Kern aufbauend, entwickeln dann viele Communities und auch Firmen ihre Derivate wie Debian oder Ubuntu.<br>
Obwohl uns Linux schon einige Jahrzehnte begleitet, hat es sich bisher zumindest auf Desktop Systemen immer noch nicht so richtig durchsetzen können. Wenn man MacOS als UNIX Derivat dazurechnen will, dann findet man aber doch eine respektable Verbreitung vor.<br>
Anders sieht das ganze auf Servern aus, hier stellt Linux wohl den Löwenanteil. Das lässt sich vorallem darauf zurückführen, dass auf Servern oft keine grafischen Oberflächen nötig sind und diese Systeme daher extrem niedrige Anforderungen an die verwendete Hardware haben. Auch hohe Lizenzkosten wie bei anderen Plattformen üblich gibt es hier nicht.</p>

<p>Es gibt natürlich noch andere prominente Vertreter, die uns schon viele Jahre begleiten. Beispiele die mir spontan einfallen sind Open- bzw. LibreOffice und Gimp, ein freies Programm zum Bearbeiten von Grafiken.</p>

<h3>Open Source heute</h3>

<p>In der heutigen Zeit gibt es kaum noch Softwarearten, die ohne starke Konkurrenz aus dem Open Source Lager auskommen. Egal ob die bereits angesprochenen Betriebssysteme, Textbearbeitungen, Tabellenkalkulationen oder auch Entwicklungsumgebungen, überall könnte ich Beispiele nennen.</p>

<p>Vorallem in den letzten Jahren haben sich dann auch große Konzerne zu einem Strategiewechsel entschieden und sind mittlerweile aktiv an bestehenden Projekten beteiligt oder haben sogar eigene Open Source Anwendungen auf den Markt geworfen.<br>
Allen voran möchte ich hier als erstes Google nennen. Viele mögen die Dominanz des Suchmaschinenherstellers verfluchen, aber wo wäre das Internet ohne die Firma aus Mountain View wenn wir an Android im mobilen Markt oder Chromium im Browsermarkt denken? Wir werden es wohl nie erfahren.<br>
Ich bewundere in den letzten Jahren auch die Offenheit, die Microsoft mittlerweile an den Tag legt. Zuerst wird das <a href="https://www.mono-project.com/">Mono Projekt</a> kurz vor dem Aus doch noch gerettet, dann wird eine extrem stabile und flexible Entwicklungsumgebung wie <a href="https://code.visualstudio.com/">Visual Studio Code</a> für die Allgemeinheit zur Verfügung gestellt und in jüngster Zeit erstaunte mich die Nachricht, auch den eigenen Browser auf eine Open Source Rendering Engine umstellen zu wollen. Dem letzten Punkt werde ich aufgrund der weitreichenden Folgen später noch einen eigenen Beitrag widmen.</p>

<p>Oft entstehen Open Source Projekte heute aus der eigenen Notwendigkeit. Immer mehr Firmen entwickeln für eigene Anforderungen Software, die dann glücklicherweise der Community zur Verfügung gestellt wird. Diese nimmt das Geschenk oft dankend an und hilft fortan bei der Weiterentwicklung oder bringt gute Vorschläge wie die Programme sinnvoll ergänzt werden können. Meiner Meinung nach haben Projekte, die eine Schnittstelle zur Pluginprogrammierung anbieten, in den letzten Jahren die steilsten Aufstiege hingelegt.<br>
Ich möchte hier auch alle ermutigen, die sich selbst nicht als eingefleischte Entwickler bezeichnen. Viele Projekte benötigen anderweitig Hilfe bei Dokumentationen oder auch Übersetzungen. Natürlich sehen viele Projektverantwortliche auch gerne Verbesserungsvorschläge die dann oft aufgenommen und umgesetzt werden.</p>

<p>Neben den eigentlichen Anwendungen gibt es aber auch sehr viele Frameworks, die überwiegend als Open Source Software entwickelt werden und damit als Basis für die eigene Software oder Webanwendung verwendet werden können. Es gibt sogar ganze Plattformen die auf Frameworks basieren und selbst wiederum als Basis für weitere Projekte dienen. Mehr dazu werde ich in einem künftigen Beitrag erzählen.</p>

<h3>Monetarisierung</h3>

<p>Falls ihr euch jetzt fragt, wie man mit frei verfügbarer Software trotzdem Geld verdienen kann, seid ihr hier genau richtig. Ich werde exemplarisch drei Wege aufzeigen, die mir spontan eingefallen sind und die sich in der Vergangenheit auch bereits bewährt haben.</p>

<h5>Supportverträge</h5>

<p>Viele Firmen spezialisieren sich darauf, die Einrichtung und Betreuung von Open Source Software für ihre Kunden zu übernehmen. Teilweise bieten die Hersteller aber auch selbst solche Dienste in Form von <abbr title="Software-as-a-Service">SaaS</abbr> an. Oft gibt es dabei auch kostenpflichtige Zusatzfunktionen, was meist als Freemium Modell bezeichnet wird. Es gibt also die Grundversion, die Open Source ist und zusätzliche Plugins, die einen Mehrwert bieten gegen Aufpreis dazu. Meist werden diese Veträge dann monatlich oder jährlich abgerechnet/verlängert. Somit haben solche Firmen bzw. Hersteller eine gewisse Planungssicherheit.<br>
Ein mir bekannter Vertreter dieser Zunft ist <a href="https://www.vtiger.com/open-source-crm/">vtiger <abbr title="Customer Relationship Management">CRM</abbr></a>.</p>

<h5>Softwareagenturen</h5>

<p>Ein weiteres Modell, welches auf Open Source setzt, sind Agenturen, die Anpassungen an Open Source Software vornehmen, um diese Systeme an die Bedürfnisse ihrer Kunden anzupassen. Ein sehr bekannter Kandidat, den Kunden oft über Agenturen anpassen und dann auch verwalten lassen ist <a href="https://typo3.org/">Typo3</a>, ein frei verfügbares <abbr title="Content Management System">CMS</abbr>, das eher kompliziert einzurichten ist. Vor einigen Jahren sind solche Agenturen wie Pilze aus dem Boden gesprießt.</p>

<h5>App Stores und Provisionen</h5>

<p>Eine weitere Möglichkeit bietet sich an, wenn man ein System entwickelt, das durch Plugins erweiterbar ist. Der Hersteller kann in diesem Fall einen App Store anbieten und in seine Anwendung integrieren. Jeder Verkauf eines kostenpflichtigen Plugins spült dann Geld in die Kassen des Unternehmens. Das ist auch die Haupteinnahmequelle von Google bei Android.</p>

<h3>Vorteile von Open Source</h3>

<p>Kommen wir nun zu den Vorteilen von Open Source. Ich habe in meinen bisherigen Ausführungen bereits ein paar Punkte kurz angeschnitten. Im Folgenden nun ein paar ausführlichere Worte zu den wichtigsten Vorteilen freier Software.</p>

<h5>Lebhafte Community</h5>

<p>Es gibt viele Projekte, die eine rege Beteiligung vorweisen können. Es muss nicht immer die Schar an Entwicklern sein, die ein Projekt voran bringt. Auch wenn viele Leute ein Projekt in ihren eigenen Anwendungen verwenden, bringt das z.B. ein zugrunde liegendes Framework weiter, da es immer stabiler wird, mit jeder Version die veröffentlicht wird. Viele Projekte sind auch für Hilfe außerhalb der Entwicklungstätigkeiten dankbar, beispielsweise für Dokumentationen oder Übersetzungen in andere Sprachen. Wenn ihr euch zutraut, bei einem Projekt mitzuarbeiten, schaut am besten mal die Liste der offenen Tickets auf GitHub durch. Viele größere Projekte markieren manchmal auch Tickets, die für Einsteiger in ein Projekt geeignet sind.</p>

<h5>Anpassbar</h5>

<p>Ein Argument, das auch immer wieder weit vorne angeführt wird, wenn es um freie Software geht, ist die Anpassbarkeit an die eigenen Bedürfnisse. Auch wenn ich immer noch überzeugt bin, dass die wenigsten Firmen wirklich viel am bestehenden Code verändern, bietet sich Unternehmen mit eigenen Entwicklern die Möglichkeit dazu. Bei manchen Projekten ist das einfacher umzusetzen, weil die Macher etwa auf saubere Schnittstellen achten. Andere wiederum sind schwieriger anzupassen und darunter leidet oft auch die Updatefähigkeit wenn neue Versionen veröffentlicht werden. Es lohnt sich daher bevor man mit eigenen Anwendungen startet, ein paar Vergleiche des Codes anzustellen und auch die Dokumentation hilft hier oft schon weiter.</p>

<h5>Viele gute Ideen</h5>

<p>Wo viele Menschen zusammen kommen, entsteht auch Raum für großartige Ideen. Auch hier kann jeder einzelne einen Beitrag zu seinem auserwählten Projekt leisten. Wenn ihr euch denkt, dass eine Funktion fehlt oder eine weitere Aktion hier und da den Ablauf vereinfachen würde, scheut euch nicht davor ein Ticket bei den Entwicklern einzureichen. Meist wird das sehr positiv aufgenommen und es starten meist ausführliche Diskussionen über Pro und Kontra einer Idee und der möglichen Umsetzungen. Denkt aber auch daran, vorher die Suche zu nutzen, vielleicht hatte jemand schon einmal eine ähnliche Idee und die wurde aus bestimmten Gründen nicht umgesetzt oder auf später verschoben.</p>

<h5>Kostenlos für Anwender</h5>

<p>Für viele Endanwender ist gerade dieser Punkt mit Sicherheit einer der wichtigsten. Ich habe ihn bewusst weiter hinten in der Liste angeführt, da für mich dieser Fakt nicht so entscheidend ist wie für manch anderen. Ich bin durchaus auch bereit, ein paar Euro zu investieren, wenn ich von einem Projekt überzeugt bin. Ein gutes Beispiel dafür ist <a href="https://fontawesome.com/">FontAwesome</a>. Als Open Source Projekt gestartet, bietet es mittlerweile die Möglichkeit für 60$ eine Pro Version zu erwerben, die wirklich ihr Geld wert ist. Auch schadet es in diesem Fall der Weiterentwicklung wohl nicht und ihr könnt sicher sein, dass es noch einige Zeit rumgeistern wird. Mehr dazu gibt es auch gleich in meinen Ausführungen zu den Nachteilen.</p>

<h5>Potentiell sicherer</h5>

<p>Weil viele Entwickler rund um den Globus sich den Quellcode der Software anschauen können, ist die Wahrscheinlichkeit sehr hoch, dass nur sehr selten klaffende Lücken im Code zurückbleiben. Und auch wenn dies mal der Fall sein sollte, wie kürzlich mit einem weit verbreiteten Node.js Paket, bekommt man alle wichtigen Informationen um darauf reagieren zu können.</p>

<h3>Nachteile von Open Source</h3>

<p>Wo Licht ist, ist auch Schatten. Natürlich gibt es auch bei Open Source gewisse Nachteile, die man nicht außer Acht lassen sollte. Ich habe hier einige zusammengestellt, die ich teilweise auch persönlich schon erfahren musste.</p>

<h5>Verweiste Projekte</h5>

<p>Einer meiner wichtigsten Kritikpunkte an freier Software ist die Tatsache, dass vor allem oft kleinere Projekte vor sich hin dümpeln, bis der Hauptentwickler entweder keine Lust oder aber auch keine Zeit mehr dafür hat. Ein guter Anhaltspunkt um hier einigermaßen auf der sicheren Seite zu sein, ist die Aktivität auf GitHub. Viele Projekte werden dort gehostet und man sieht anhand der Releasefrequenzen und dem Verhältnis von offenen zu geschlossenen Tickets sehr häufig, wie eine Software gepflegt wird. In den seltensten Fällen ist ein Projekt noch aktiv, wenn seit zwei oder drei Jahren nichts mehr passiert ist oder aber fünfmal so viele Tickets offen als geschlossen sind.</p>

<h5>Lizenzprobleme</h5>

<p>Leider ist gerade in der Open Source Szene ein ziemlich undurchsichtiger Dschungel an möglichen Lizenzen für die einzelnen Projekte entstanden. Sehr häufig gibt es auch Rechtsstreitigkeiten über die Verwendung von freier Software in bestimmten Anwendungen. Dazu kommt, dass manche der verfügbaren Lizenzen oft inkompatibel sind. Ich für meinen Teil werde alles was ich veröffentliche unter die <a href="https://de.wikipedia.org/wiki/MIT-Lizenz">MIT Lizenz</a> stellen. Diese bietet für mich und euch die meisten Freiheiten und darum geht es mir ja, wenn ich Software für die Allgemeinheit zur Verfügung stellen möchte.</p>

<h5>Falsche Pferde</h5>

<p>Gerade wenn ein Hype neu aufkommt und viele Projekte ihn aufgreifen, kann es natürlich sein, dass man von Anfang an dabei sein will und man auf das falsche Pferd setzt. Oft ist es dann während der eigenen Entwicklung sehr aufwendig die verwendete Software auszutauschen. Hier hilft es meist, den Ansturm zu Beginn abzuwarten und sich erst dann für eine Umsetzung zu entscheiden, wenn sich herauskristallisiert, wer sich durchsetzen wird. Man kann die Zeit häufig nutzen, um die eigenen Ideen weiter auszudefinieren. Damit kann man auch sicherstellen, dass die eigene Idee nicht nach hinten los geht.</p>

<h5>Uneinigkeiten</h5>

<p>In der Vergangenheit ist es immer wieder passiert, dass sich die Verantwortlichen eines Projekts nicht ganz einig sind über den weiteren Kurs ihres Projekts. Prominente Vertreter waren z.B. Node.js, Open- und LibreOffice oder auch MySQL und MariaDB. Während die letztgenannten zu den wenigen Ausnahmen gehören, ist es häuft so, dass eines der beiden Projekte auf der Strecke bleibt und die Entwicklung stark unter der Auseinandersetzung leidet.</p>

<h5>Schwierigere Monetarisierung</h5>

<p>Diesen Punkt möchte ich der Vollständigkeit halber erwähnen. Ich habe ja bereits ausführlich über mögliche Wege, um mit freier Software Geld zu verdienen, gesprochen. Mit Spannung werde ich jedenfalls verfolgen, wie sich <a href="https://expressionengine.com/">ExpressionEngine</a> (die Software hinter diesem Blog) entwickeln wird, nachdem es mit der aktuellen Version zur Open Source Schiene gewechselt ist.</p>

<h5>Potentiell gefährdet</h5>

<p>Natürlich kann die Offenheit des Quellcodes auch Bösewichte anlocken, die versuchen ein Projekt zu kompromittieren. Gerade deshalb ist der Zusammenhalt der Community auch so wichtig. Zusammen sind wir stark!</p>

<h3>Fazit</h3>

<p>Open Source Software hat einen langen Weg hinter sich und hoffentlich auch vor sich. Ich bin aber davon überzeugt, dass eher mehr als weniger Projekte in Zukunft auf Open Source setzen werden. Ich bin jedenfalls dabei und werde auch meine Entwicklung soweit es mir möglich ist unter einer freien Lizenz veröffentlichen. Ich hoffe ich konnte euch einen kleinen Einblick geben und wünsche mir das sich der ein oder andere nach dem Lesen nun dazu entscheidet, auch an einem Projekt mitzuarbeiten.</p>

<p>Nun will ich euch aber nicht länger aufhalten. Wir lesen uns dann im nächsten Beitrag.</p>

      ]]></content>
    </entry>    <entry>
      <title><![CDATA[Willkomen auf Next Direction]]></title>
      <link rel="alternate" type="text/html" href="https://next-direction.de/posts/willkomen-auf-next-direction" />
      <id>tag:https:2018:/next-direction.de/2.3</id>
      <published>2018-12-15T18:30:00Z</published>
      <updated>2019-02-03T10:41:51Z</updated>
      <author>
            <name>Manuel</name>
            <email>manuel@next-direction.de</email>
            
      </author>
      <image>https://next-direction.de/images/uploads/welcome.jpeg</image>

      <content type="html"><![CDATA[
        <img src="https://next-direction.de/images/uploads/welcome.jpeg" alt="Teaser - Willkommen auf Next Direction"/><br>
        <p>Hallo liebe Leser und herzlich willkommen auf Next Direction. In den folgenden Zeilen will ich euch erzählen, wer ich bin, was ich hier mache und was ihr in Zukunft hier zu erwarten habt. Dabei werde ich einige Elemente von Next Direction genauer beleuchten und einen Ausblick auf zukünftige Themen, die ich hier behandeln möchte, geben.</p>

<h3>First things first</h3>

<p>Fangen wir also vorne an. Ich heiße Manuel, bin vom Alter her mittlerweile in den 30ern angekommen und seit über acht Jahren als Softwareentwickler angestellt.</p>

<p>Ursprünglich habe ich eine Ausbildung als Fachinformatiker mit Fachrichtung Systemintegration gemacht. Während dieser Zeit hat sich dann aber meine wahre Passion herauskristallisiert, die ganz klar im Bereich der Entwicklung liegt. Ich habe deshalb im Anschluss an meine Ausbildung ein Studium der Informatik mit Schwerpunkt Softwareentwicklung absolviert. Da ich nebenbei privat bereits im Bereich Webentwicklung Erfahrungen gesammelt habe, war für mich früh klar, dass ich auch beruflich in diese Richtung gehen will. Bald werde ich die zehn Jahre voll machen und bisher bin ich sehr glücklich mit meiner damaligen Entscheidung.</p>

<h3>Warum Next Direction</h3>

<p>Kommen wir nun also zu meinem neuesten Projekt, Next Direction. Nachdem ich mich die letzten Jahre mehr oder weniger in einer Blase bewegt habe und nur die Sachen eingesetzt habe, die ich für meine tägliche Arbeit brauchte, habe ich mich Anfang des Jahres dazu entschieden mal über den Tellerrand hinaus zu schauen, was es da sonst noch so gibt. Ich habe dabei viele neue Themen für mich entdeckt und will euch nun daran teilhaben lassen. Daher habe ich diesen Blog ins Leben gerufen.</p>

<p>Zwei dieser Neuentdeckungen, denen ich später auch noch eigene Beiträge widmen werde, haben mich dann auch zum Namen <em>Next Direction</em> inspiriert.</p>

<ul>
<li>Die erste ist <a href="https://nuxtjs.org/">Nuxt.js</a> woraus ich einfach Next gemacht habe. Nuxt.js wurde auf Basis von <a href="https://vuejs.org/">Vue.js</a> entwickelt und sorgt für eine beschleunigte Entwicklung von Frontends mit dem zugrunde liegenden Framework. </li>
<li>Die zweite ist <a href="https://directus.io/">Directus</a>. Dabei handelt es sich um ein <a href="https://en.wikipedia.org/wiki/Headless_content_management_system">Headless CMS</a>, welches die Erstellung von Inhalten im Backend übernimmt und eine Schnittstelle bereitstellt um beliebige Frontends entwickeln zu können.</li>
</ul>

<h3>Logo und Farben</h3>

<p>Werfen wir als nächstes einen Blick auf das Logo. Eher durch Zufall entstanden vereint es zwei entscheidende Prinzipien von Next Direction. Das Logo ist eine Kombination aus einem Pfeil, der in die richtige Richtung weisen soll und einer Zielscheibe, um anzudeuten, dass am Ende immer das Ergebnis zählt. Ich hoffe, dass ich euch mit diesem Blog mit Beidem unterstützen kann und würde mich freuen, wenn ihr mir eure Ergebnisse über das Kontaktformular im Fußbereich schicken würdet.</p>

<p>Beim Thema Farben gibt es keine solch tiefgründige Erklärung, dunkelblau und grau sind meine beiden Lieblingsfarben und ich habe einfach nach einer Kombination gesucht, die ein schönes Gesamtbild ergibt. Ich habe versucht diese beiden Farben durchgehend zu verwenden und es gibt nur sehr wenige Ausnahmen von dieser Regel.</p>

<h3>Endlich wird&#8217;s technisch</h3>

<p>Falls ihr euch jetzt fragt, ob ich all das hier selber gemacht habe, muss ich gestehen, dass ich mich hier nur zur Hälfte rühmen kann. Als Basis für diesen Blog bin ich nach ein klein wenig Recherche auf das CMS System <a href="https://expressionengine.com/">ExpressionEngine</a> gestoßen und war nach kurzem Studium der Dokumentation davon überzeugt, dass sich damit großartige Projekte realisieren lassen. Ich muss allerdings auch eine Vorwarnung aussprechen, denn ohne entsprechendes Grundwissen in den Sprachen des Webs wie HTML und CSS werdet ihr damit Schwierigkeiten bekommen. Es gibt nämlich kaum fertige Templates bzw. Themes mit denen man was anfangen kann. Aber seien wir ehrlich, so was selbst gemachtes begeistert einen doch immer noch am meisten.</p>

<p>Bei ExpressionEngine erstellt man über das Backend sogenannte Channels, was nichts anderes ist wie eine Sammlung von gleichen Datensätzen. In meinem Fall gibt es einen Channel namens <strong>site</strong> und einen namens <strong>posts</strong>. Posts sollte soweit klar sein, damit werden die Beiträge dieses Blogs abgebildet. Alle anderen Seiten wie das <a href="https://next-direction.de/site/imprint">Impressum</a> oder die <a href="https://next-direction.de/site/privacy">Datenschutzerklärung</a> werden im erstgenannten Channel gespeichert.</p>

<p>Auf Basis dieser Channels werden dann die HTML Templates erstellt und mittels CSS das Aussehen der Seite entsprechend den eigenen Wünschen angepasst. ExpressionEngine bietet dazu viele verschiedene Platzhalter für den HTML Inhalt an, um die Ausgabe der Daten aus dem Backend dynamisch zu gestalten. Hier muss man sich in jedem Fall ausgiebig mit der Dokumenation beschäftigen um den vollen Funktionsumfang auch nutzen zu können. Nur dann lässt sich die Seite wirklich auf alle Bedürfnisse anpassen.</p>

<p>Natürlich habe ich im Vorfeld auch noch andere Kandidaten getestet bzw. auch schon in anderen Projekten im Einsatz. Ich möchte exemplarisch <a href="https://www.joomla.org/">Joomla</a> und <a href="https://typo3.org/">Typo3</a> herausgreifen und ein paar Gründe nennen, die mich letztendlich in meiner Entscheidung für ExpressionEngine bestätigt haben. Ich denke beide Kandidaten dürften dem ein oder anderen schon über den Weg gelaufen sein.<br>
Während ich Joomla durchaus für kleinere Hobbyprojekte empfehlen kann, würde ich bei professionellen Webseiten eher zu anderen Produkten greifen. Ich finde vor allem das Backend ist ziemlich unübersichtlich und wirkt mir persönlich fast zu verspielt. Im Frontend Bereich wiederum gibt es zwar eine ganze Menge an vorgefertigten Themes und Designs. Zum einen sehen die aber auch aus, als wären sie schon 1000-fach verwendet worden. Zum anderen wird es mit Updates schwierig, solltet ihr planen ein solches Design anzupassen um daraus euer eigenes zu machen, was ich leider bei meinem ersten Joomla Projekt so gemacht habe.<br>
Ich muss gestehen, mit Typo3 habe ich bisher nur etwas gespielt und die ein oder andere Sache ausprobiert. Etwas produktives ist dabei bisher noch nicht herausgesprungen. So sehr ich mich auch immer wieder damit befassen möchte, so sehr schreckt mich TypoScript jedes Mal wieder ab. Für alle die es nicht kennen, TypoScript ist eine Konfigurationssprache, die in Typo3 zwingend erforderlich ist, wenn man damit seriöse Webauftritte erstellen möchte. Leider ist für mich bisher kein wirkliches Muster in dieser Sprache zu erkennen und man muss die Dokumentation deshalb schon fast auswendig lernen. Ich will aber auf jeden Fall in den nächsten Jahren ein Projekt damit umsetzen und werde euch dann natürlich an meinem Weg teilhaben lassen.</p>

<p>ExpressionEngine vereint für mich persönlich die Vorteile beider Systeme, ohne dass wirkliche Nachteile in Kauf genommen werden müssen. Wenn ihr euch die Mühe macht, euch ein bisschen mit dem System zu beschäftigen, verspreche ich euch, dass auf jeden Fall super Ergebnisse am Horizont warten. Und das beste habe ich bisher noch gar nicht erwähnt, denn ExpressionEngine ist seit kurzem in der Version 5 verfügbar und wurde mit dieser Version auch als Open Source Software veröffentlicht. Wer also Lust hat, kann sich aktiv an der Entwicklung beteiligen.</p>

<h3>Navigation auf Next Direction</h3>

<p>Es gibt im wesentlichen vier Bereiche der Seite, die ich euch kurz vorstellen möchte, damit ihr euch hier zurecht findet und euch wohl fühlt. Diese wären:</p>

<ul>
<li><strong>Startseite:</strong> Hier findet ihr immer die aktuellsten Beiträge. Ihr könnt jederzeit über einen Klick auf das Logo bzw. den Blognamen dorthin zurück kehren.</li>
<li><strong>Alle Beiträge:</strong> Diese Seite erreicht ihr über den Link am Ende der Startseite. Hier werden immer zehn Beiträge pro Seite, absteigend nach Veröffentlichungsdatum sortiert, angezeigt.</li>
<li><strong>Einzelbeitrag:</strong> In allen genannten Übersichten der Beiträge ist jeweils ein <em>weiterlesen</em> Link zum speziellen Beitrag. Außerdem sind immer die Bilder und Titel der Beiträge verlinkt.</li>
<li><strong>Suche:</strong> Ihr habt einen Beitrag gelesen und findet ihn nicht mehr? Kein Problem! Rechts oben könnt ihr jederzeit über alle Beiträge suchen. Beachtet bitte, dass alle gesuchten Wörter im Beitrag vorkommen müssen um als Treffer durchzugehen.</li>
</ul>

<h5>Keine Kommentare</h5>

<p>Leider kann ich derzeit keine Kommentarfunktion anbieten. Nicht weil ich nicht will oder es technisch nicht möglich ist, sondern weil mir hier die rechtliche Situation in den letzten Jahren zu schwierig geworden ist und ich alleine auch nicht in der Lage bin, alle Kommentare zeitnah zu überprüfen. Solltet ihr trotzdem mit mir in Kontakt treten wollen, findet ihr im Fußbereich der Seite das Kontaktformular.</p>

<h3>Ausblick</h3>

<p>Nachdem nun die Theorie abgearbeitet ist, kommen wir zum Ausblick und was ich die nächsten Jahre alles mit Next Direction vor habe. Primär ist es für mich eigentlich ein Kanal, um mein gesammeltes Wissen mit euch zu teilen. Ich werde mich nicht auf ein spezielles Thema oder eine spezielle Programmiersprache festlegen. Vielmehr möchte ich verschiedenste etablierte aber auch hochaktuelle Themen rund um die Webentwicklung aufgreifen und euch auf verständliche Weise näher bringen. Ich werde dabei meinen Fokus verstärkt auf frei verfügbare Software setzen.</p>

<p>Sollte euch was besonders gefallen oder euch etwas stören, könnt ihr mich jederzeit über das Kontaktformular erreichen. Das befindet sich wie einige andere Links im Fußbereich der Seite. Natürlich freue ich mich auch immer über Anregungen oder Wünsche zu Themen die euch interessieren.</p>

<p>Ich habe mich dazu entschieden als Versionskontrollsystem auf GIT zu setzen und werde dafür GitHub nutzen. Ich wollte zuerst Bitbucket zusätzlich verwenden, nachdem nun aber GitHub auch private Projekte kostenlos anbietet, gibt es dafür aktuell keinen Grund mehr. Ihr findet den Link zu meiner GitHub Seite in der Fußzeile.</p>

<p><strong>Update 03.02.2019</strong>: Next Direction bietet jetzt auch eine Slack Community. Der Beitritt erfolgt über den entsprechenden Link im Fußbereich. Ich würde mir wünschen, dass ihr beitretet und wir einen regen Austausch über alle möglichen Entwicklungsthemen starten.</p>

<p>Nun will ich euch aber nicht mehr länger aufhalten und wünsche euch viel Spaß hier auf meinem Blog und freue mich auf unsere gemeinsame Zeit. Natürlich könnt ihr mir auch jederzeit auf Twitter folgen und den RSS Feed dieses Blogs abonnieren, um immer über neue Beiträge auf dem Laufenden gehalten zu werden. Es gibt die nächste Zeit auf jeden Fall schon mehr als genug Stoff, den ich euch vorstellen möchte, seid also gespannt!</p>

      ]]></content>
    </entry>

</feed>